第7章 Java常用类
Java之所以比较受人青睐,不仅仅是因为它具有面向对象的特性和易懂的语法,Java本身还带有Runtime、System、Math、Random、Calendar、Pattern和Matcher等工具类,强大的类库给Java提供了强大的功能,用户可以应用这些类库方便地实现一些复杂的功能。掌握了这些类的使用,将大大降低程序员在实际编程中的工作量。
实例43 数字的舍入
在数学中,常常需要对数字进行舍入,如四舍五入。在Java程序中,可以通过java.math.BigDecimal类来实现,java.math.BigInteger和java.math.BigDecimal类能够表示任意大的数字,专门用于高精度的运算;Java还可以实现十进制转换成二进制、八进制、十六进制等操作;利用java.util.Random类能够生成一系列的随机数。本实例主要介绍对小数进行舍入,支持多种舍入模式,如四舍五入等。返回结果可以是整数,也可以是小数并且能够指定舍入的小数位数。
技术要点
BigDecimal类的setScale(int newScale, int roundingMode)方法可以完成舍入,其舍入结果可以是整数,也可以是小数。其中参数newScale表示要返回的BigDecimal值的标度;roundingMode表示表示舍入方式,它支持的舍入方式如下。
• ROUND_HALF_UP:四舍五入。
• ROUND_CEILING:接近正无穷大的舍入模式。
• ROUND_DOWN:接近零的舍入模式。
• ROUND_UP:舍入远离零的舍入模式。
• ROUND_FLOOR:接近负无穷大的舍入。
• ROUND_HALF_DOWN:向“最接近的”数字舍入,如果与两个相邻数字的距离相等,则为向上舍入的舍入模式。
• ROUND_HALF_EVEN:向“最接近的”数字舍入,如果与两个相邻数字的距离相等,则向相邻的偶数舍入。
• ROUND_HALF_UP:向“最接近的”数字舍入,如果与两个相邻数字的距离相等,则为向上舍入的舍入模式。
实现步骤
(1)新建一个类名为Math_Round.java。
(2)代码如下所示:
package chp07; import java.math.BigDecimal; import java.util.Scanner; /** * 对数字进行各种模式的舍入,如四舍五入等 */ public class Math_Round { public static long getTraRoundMath(double du) { //Math.round方法采用首先将dou加上0.5,然后取下整数。 //dou = 4.6, 首先dou加0.5成5.1,其下整数为5。四舍五入的结果就是5 return Math.round(du); } public static long getIntRound(double dou, int mode) { //最后取得BigDecimal对象转化成int并返回 return Math_Round.getRound(dou, 0, mode).longValue(); } /** * 要求返回舍入后结果为BigDecimal类型的数据;其中,du为舍入的数字,scale表示返回的BigDecimal 对象的标度(scale),mode表示舍入模式,return表示返回舍入后的结果,即一个BigDecimal数据。 */ public static BigDecimal getRound(double du, int scale, int mode) { //创建一个新的BigDecimal对象paramNumber,该对象的值和dou大小一样 BigDecimal paramNumber = new BigDecimal(du); //然后调用paramNumber的setScale方法,该方法返回一个BigDecimal对象temp //返回值的标度为第一个参数指定的值,标度为大小表示小数部分的位数 //第二个参数指定了paramNumber对象到temp对象的舍入模式,如四舍五入等 return paramNumber.setScale(scale, mode); //return new BigDecimal(du).setScale(0, mode); //可以用一条语句实现 } public static int roundmode(int c) { int r = 0; switch (c) { case 0: r = BigDecimal.ROUND_CEILING; break; case 1: r = BigDecimal.ROUND_DOWN; break; case 2: r = BigDecimal.ROUND_UP; break; case 3: r = BigDecimal.ROUND_FLOOR; break; case 4: r = BigDecimal.ROUND_HALF_DOWN; break; case 5: r = BigDecimal.ROUND_HALF_EVEN; break; case 6: r = BigDecimal.ROUND_HALF_UP; break; default: r = c; } return r; } public static String print(int c) { String ss = ""; switch (c) { case 0: ss = "舍入模式ROUND_CEILING:"; break; case 1: ss = "舍入模式ROUND_DOWN:"; break; case 2: ss = "舍入模式ROUND_UP:"; break; case 3: ss = "舍入模式ROUND_FLOOR:"; break; case 4: ss = "舍入模式ROUND_HALF_DOWN:"; break; case 5: ss = "舍入模式ROUND_HALF_EVEN:"; break; case 6: ss = "舍入模式ROUND_HALF_UP:"; break; default: ss = "Error"; } return ss; } public static void main(String[] args) { System.out.println("请输入待舍入的数字:"); Scanner sc = new Scanner(System.in); double d1 = sc.nextDouble(); System.out.println("舍入模式编号如下所示:\n" + " 0 ROUND_CEILING\n" + " 1 ROUND_DOWN\n" + " 2 ROUND_UP\n" + " 3 ROUND_FLOOR\n" + " 4 ROUND_HALF_DOWN\n" + " 5 ROUND_HALF_EVEN\n" + " 6 ROUND_HALF_UP\n"); System.out.println("请输入舍入模式编号和需保留的小数位,并用(,)号隔开:"); Scanner sc1 = new Scanner(System.in); String str = sc1.next(); System.out.println("待舍入的数字:" + d1); if (str.indexOf(",") != 0) { String[] s = str.split(","); int n1 = Integer.valueOf(s[0]).intValue(); //舍入模式 int n2 = Integer.valueOf(s[1]).intValue(); //保留的小数位 int r = roundmode(n1); System.out.println("要求舍入后返回数字的小数部分保留" + n2 + "位:"); if (print(n1).equals("Error")) { System.out.println("舍入模式编号错误"); return; } System.out.println(print(n1) + "\t" + getRound(d1, n2, r)); } else { int n1 = Integer.valueOf(str).intValue(); //舍入模式 int r = roundmode(n1); System.out.println("要求舍入后返回整数:"); if (print(n1).equals("Error")) { System.out.println("舍入模式编号错误"); return; } System.out.println(print(n1) + "\t" + getIntRound(d1, r)); System.out.println("采用Math类实现四舍五入的结果: \t" + Math_Round.getTraRoundMath(d1)); } } }
(3)运行结果如图7-1所示。
图7-1 运行结果
源程序解读
(1)getTraRoundMath方法用Math类的round方法实现了传统意义上的四舍五入功能,传入一个任意小数,对它进行四舍五入后,返回舍入后的整数。
(2)getTraRound方法用BigDecimal的setScale方法实现了传统意义上的四舍五入功能,传入一个任意小数,对它进行四舍五入后,返回舍入后的整数。调用setScale方法时,将小数位数设为0,表示舍入到整数,把舍入模式设为ROUND_HALF_UP,表示进行四舍五入。
(3)getIntRound方法用BigDecimal的setScale方法实现数字的各种舍入,支持各种舍入模式,但返回的结果为整数。
(4)getRound方法为getTraRound和getIntRound方法的最终实现,参数为待舍入的数字、舍入后数字的小数位数和舍入模式。
(5)roundmode方法是根据传入参数来选择舍入模式。例如,传入的参数c为0,则选择的就是ROUND_CEILING(接近正无穷大的舍入模式)。
(6)print方法同(5),是根据传入参数来选择输出舍入模式。
实例44 转换数字的进制
在现实生活中用到的数字都是用十进制表示的,但是计算机所识别的是二进制,所以,在编程的过程中,有时就需要将人类所识别的数字转换成计算机所识别的数字,这就需要转换数字的进制,一个数字可以用任意进制表示。本实例介绍如何转换数字的进制,实现二进制、八进制、十进制、十六进制之间的互相转换。
技术要点
实现数字进制转换的关键技术点如下:
• 使用Integer或Long的toBinaryString(int i)方法将整数转换成二进制。其中参数i表示要转换成字符串的整数。
• 使用Integer或Long的toOctalString(int i)方法将整数转换成八进制。其中参数i表示要转换成字符串的整数。
• 使用Integer或Long的toHexString(int i)方法将整数转换成十六进制。其中参数i表示要转换成字符串的整数。
• Java中在声明数字时默认采用的是十进制,可以在数字前加上特定的符号表示数字采用八进制或者十六进制。数字前面加数字0(零)表示该数字是八进制,例如:012。加0x (零x)表示该数字是十六进制。例如:0x00FF。
• Java的整型封装类Integer和Long提供toString(int i, int radix)静态方法,可以将一个任意进制的整数转换成其他任意进制的整数。其中参数i表示要转换成字符串的整数,radix表示用于字符串表示形式的基数。
实现步骤
(1)新建一个类名为M_Conversion.java。
(2)代码如下所示:
package chp07; /** * 描述数字的进制,有八进制、十进制和十六进制 */ public class M_Conversion { public static void main(String[] args) { int O_data = 0766; //八进制数字的声明,在前面加上0(零) int T_data = 123456; //十进制的声明 int H_data = 0xA57B; //十六进制数字的声明,在前面加上0x(零x),x不区分大小写 System.out.print("八进制" + O_data + "转换成二进制:"); //八进制转换成二进制 System.out.print(Integer.toString(O_data, 2) + "; "); System.out.println(Integer.toBinaryString(O_data)); System.out.print("八进制" + O_data + "转换成十进制:"); //八进制转换成十进制 System.out.print(Integer.toString(O_data, 10) + ";"); System.out.println(Integer.toString(O_data)); System.out.print("八进制" + O_data + "转换成十六进制:"); //八进制转换成十六进制 System.out.print(Integer.toString(O_data, 16) + "; "); System.out.println(Integer.toHexString(O_data)); System.out.print("八进制" + O_data + "转换成九进制:"); //还可以转换成其他进制 System.out.println(Integer.toString(O_data, 9)+" \n"); System.out.print("十进制" + T_data + "转换成十六进制:"); //同样可以将十进制、十六进制转换成其他任意进制的数字 System.out.print(Integer.toString(T_data, 16) + "; \n"); System.out.print("十进制 " + T_data + "转换成八进制:"); System.out.println(Integer.toOctalString(T_data)); System.out.print("十六进制 " + H_data + "转换成十进制:"); System.out.println(Integer.toString(H_data, 10)); System.out.print("十六进制 " + H_data + "转换成二进制:"); System.out.print(Integer.toBinaryString(H_data) + "; "); System.out.println(Long.toBinaryString(H_data)); } }
(3)运行结果如图7-2所示。
图7-2 运行结果
实例45 随机数
在Java程序中会经常使用随机数产生一系列的动态效果,例如:用随机数生成不同的颜色。本实例主要介绍如何随机生成指定范围内随机数序列。
技术要点
生成随机数有两个方法。
(1)使用java.util.Math类。它的random方法生成随机数,为0~1之间的浮点数。
(2)使用java.util.Random类。它通过其构造方法创建随机数生成器,然后调用不同的方法获取多种类型随机数,而且随机数范围不限。它可以获取随机数的方法有:
• nextBoolean()。返回下一个伪随机数,它是此随机数生成器的序列中均匀分布的boolean值。
• nextBytes(byte[] bytes)。生成随机字节并将其置于用户提供的字节数组中。所生成的随机字节数等于该字节数组的长度。
• nextFloat()。返回下一个伪随机数,它是从此随机数生成器的序列中取出的在0.0和1.0之间均匀分布的float值。
• nextInt()。它有两种形式—带参数和无参数。无参则表示返回下一个伪随机数,它是此随机数生成器的序列中均匀分布的int值。nextInt(int n)中参数n表示所返回随机数的范围而且必须为正数,结果返回一个伪随机数,处于0<=x<=n之间均匀分布的int值。
• nextLong()。返回下一个伪随机数,它是此随机数生成器的序列中均匀分布的long值。
• nextDouble()。返回下一个伪随机数,它是从此随机数生成器的序列中取出的在0.0~1.0之间均匀分布的double值。
• setSeed(long seed)。使用单个long种子设置此随机数生成器的种子,其中参数seed表示初始种子。
实现步骤
(1)新建一个类名为TextOperatorArray.java。
(2)代码如下所示:
package chp07; import java.util.Random; /** * Java实用工具类库中的类java.util.Random提供了产生各种类型随机数的方法。 * 它可以产生int、long、float、double以及Goussian等类型的随机数。 * java.lang.Math中的方法random()只产生double型的随机数。 */ public class Random_Number {
public static void R_Math() {//使用java.lang.Math的random方法生成随机数 System.out.println("使用Math类中的random方法生成随机数的示例如下:"); for (int i = 0; i < 5; i++) { int n = (int) (Math.random() * 50000); System.out.print((char) n + "\t"); //将数字转换成字符输出 } System.out.println("\n"); } public static void N_Param() {//使用不带参数的构造方法构造java.util.Random对象 System.out.println("使用Random类的构造方法生成随机数的示例如下:"); Random rdm = new Random(); //产生各种类型的随机数 System.out.println("int: " + rdm.nextInt()); //按均匀分布产生整数 System.out.println("long: " + rdm.nextLong()); //按均匀分布产生长整数 System.out.println("float: " + rdm.nextFloat()); //按均匀分布产生大于等于0,小于1的float System.out.println("double: " + rdm.nextDouble()); //按均匀分布产生[0,1)范围的double System.out.println("Gaussian:" + rdm.nextGaussian()); //按正态分布产生随机数 System.out.print("随机整数序列:"); //生成一系列随机数 for (int i = 0; i <=5; i++) { System.out.print(rdm.nextInt() + " "); } System.out.println("\n"); } public static void Range() { //指定随机数产生的范围 System.out.println("使用指定范围产生随机序列的示例如下:"); System.out.print("[0,5)范围内随机整数序列: "); Random rdm = new Random(); for (int i = 0; i < 10; i++) { System.out.print(rdm.nextInt(5) + " "); //Random的nextInt(int n)方法返回一个[0, n]范围内的随机数 } System.out.println(); System.out.print("[3,24)范围内随机整数序列: "); for (int i = 0; i < 10; i++) { //因为nextInt(intn)方法的范围是从0开始的,所以需要把区间[3,24)转换成3+[0, 21) System.out.print(3 + rdm.nextInt(21) + " "); } System.out.println(); System.out.print("利用nextFloat()生成[0,55)范围内的随机整数序列: "); for (int i = 0; i < 10; i++) { System.out.print((int) (rdm.nextFloat() * 50) + " "); } System.out.println("\n"); } public static void H_Param() { //使用带参数的构造方法构造Random对象 //构造函数的参数是long类型,是生成随机数的种子 System.out.println("使用带参数的构造方法构造的Random对象的示例如下:"); Random rad = new Random(5); //对于种子相同的Random对象,生成的随机数序列是一样的 System.out.println("使用种子为5的Random对象生成[0,5)内随机整数序列: "); for (int i = 0; i < 10; i++) { System.out.print(rad.nextInt(5) + " "); } System.out.println(); Random rad1 = new Random(5); System.out.println("使用另一个种子为5的Random对象生成[0,5)内随机整数序列: "); for (int i = 0; i < 10; i++) { System.out.print(rad1.nextInt(5) + " "); } System.out.println("\n"); } public static void main(String[] args) { R_Math(); Range(); N_Param(); H_Param(); } }
(3)运行结果如图7-3所示。
图7-3 运行结果
源程序解读
(1)R_Math方法是利用Math的random静态方法随机生成一个0~1之间的浮点数。
(2)H_Param方法是利用Random对象根据种子生成随机数,种子相同的Random对象,无论何时运行它,也无论它在哪台机器上,生成的随机数序列是相同的。只有种子不同的Random对象,生成的随机数序列是不一样的。
(3)N_Param方法利用默认构造方法构造Random对象,生成的随机数的种子是系统当前时间的毫秒数,因此,用这种Random对象生成的随机数序列很少出现相同的,除非连续运行它的时间足够短。
(4)Range方法是利用Random对象生成随机数,其范围都是从0开始的,如果需要获取其他范围的随机数,需要对区间进行变换。例如,区间[a, b)等价于a + [0, b-a),有时也可以使用乘法。
实例46 Java Applet绘制心形曲线
非常有名的笛卡儿曲线数学公式:(x2+y2-2ax)2=4a2(x2+y2),即心形曲线,在本实例中通过Applet绘制出笛卡儿曲线。
技术要点
笛卡儿曲线是一个圆在同样半径的圆周上滚动,在滚动的过程中一定会形成轨迹曲线。它的数学方程为x=a(2cos(t)-cos(2t));y=a(2sin(t)-sin(2t));r=2a(1+cos(θ))。
算法实现:r =Math. PI/45*i*(1-Math.sina(Math.PI/45*j))*18;x=r*Math.cos(Math.PI/45*j)*Math.sina(Math.PI/45*i)+AppletWidth/2;y=r*Math.sin(Math.PI/45*j)+AppletHeight/4。
实现步骤
(1)新建一个类名为CartesianCurve.java。
(2)代码如下所示:
package chp07; import java.applet.*; import java.awt.*; public class CartesianCurve extends Applet { int width, height; //声明int类型变量 Image image; //声明图像变量 Graphics draw_Curve; //声明图形绘制变量 public void init() { //Applect程序初始化 setBackground(Color.black); this.setSize(350, 310); width = getSize().width; height = getSize().height; image = createImage(width, height); draw_Curve = image.getGraphics(); } public void paint(Graphics g) { //利用Graphics绘制组件 draw_Curve.clearRect(0, 0, width, height); //用红色来填充清除指定的矩形 draw_Curve.setColor(Color.red); int i, j; double x, y, r; for (i = 0; i <= 90; i++) //笛卡儿数学公式:(x*x+y*y-2ax)2=4a*a(x*x+y*y) for (j = 0; j <= 90; j++) { r = Math.PI / 45 * i * (1 - Math.sin(Math.PI / 45 * j)) * 18; x = r * Math.cos(Math.PI / 45 * j) * Math.sin(Math.PI /45 * i) + width / 2; y = -r * Math.sin(Math.PI / 45 * j) + height / 4; draw_Curve.fillOval((int) x, (int) y, 2, 2); } g.drawImage(image, 0, 0, this); } }
(3)运行结果如图7-4所示。
图7-4 运行结果
源程序解读
(1)init方法,对Applet程序进行初始化,当Applet首先被加载时,该方法会自动被调用一次。
(2)paint方法,将Graphics对象画出的图像打印出来。
(3)Math类的方法说明:Math.PI是Math的一个final常量,表示比任何其他值更接近圆的周长与直径之比pi;Math.sin()表示返回角的三角正弦;Math.cos()表示返回角的三角余弦。
实例47 简单的计算器
计算器相信对任何一个人来说,都不会感觉到陌生,那么,自己是否亲手制作过一个简单的计算器呢?本实例将介绍如何实现一个计算器,该计算器的功能与Windows操作系统的附件所带的计算器相似,支持简单的加、减、乘、除运算。
技术要点
实现计算器的技术要点如下:
• 用JFrame作为计算器的主界面,实现ActionListener接口,处理按键事件。
• 将组件按照BorderLayout布局方式添加到面板上,然后再将面板放入JFrame中。
• 用文本框JTextField存放计算结果,并通过它的setText和getText设置获取文本框中的字符串。
• 计算器上的按键用按钮JButton实现,并通过addListener方法为所有按键添加事件监听器。
实现步骤
(1)新建一个类名为CalculatorDemo.java。
(2)代码如下所示:
package chp07; import java.awt.*; import java.awt.event.*; import java.text.DecimalFormat; import javax.swing.*; public class CalculatorDemo extends JFrame implements ActionListener { //导入动作监听接口 JTextField text_Show; //设计面板中的单位 JPanel pnl, pnl_1, pnl_2, pnl_3; JMenuBar menu; //没有起到任何的作用,用来作布局控制 JTextField text; JLabel label; //label单纯做摆设,控制面板的形状 JButton btn_Bk, btn_CE, btn_C; JButton btn[]; JButton btn_MC, btn_MR, btn_MS, btn_MAdd; JButton btn_Dot, btn_AddAndSub, btn_Add, btn_Sub, btn_Mul, btn_Div,btn_Mod; JButton btn_Sqrt, btn_Dao, btn_Equal; DecimalFormat df; //设置数据输出精度 boolean click_Flag; //控制当前能否按键 double memory_data; //使用内存中存储的数字 int memory_int; //int类型存储器,只要是int型的数据就存进来 double middle, result; //用来保存double型数据的中间值(middle)和最后结果(result) short key = -1, prekey = -1; //key用来保存当前进行何种运算,prekey用来保存前次进行何种运算 public CalculatorDemo() { //构造函数 super("自制计算器"); click_Flag = true; result = 0; setDefaultCloseOperation(3); Image image = getToolkit().getImage("D:/workspace/New/chp07/2.jpg :); setIconImage(image); df = new DecimalFormat("0.##############");//设置数据输出精度(对于double型值) text_Show = new JTextField(15); text_Show.setText(""); text_Show.setHorizontalAlignment(JTextField.RIGHT); text_Show.setEditable(false); text_Show.setBackground(Color.white); pnl = new JPanel(); this.getContentPane().add(pnl); pnl_1 = new JPanel(); pnl_2 = new JPanel(); pnl.setLayout(new BorderLayout()); menu = new JMenuBar(); //设计整个面板 //面板pnl装载显示结果的文本区和面板pnl_1 //面板pnl_1按BorderLayout的布局方式装载面板pnl_2和面板pnl_3 pnl.add(menu, BorderLayout.NORTH); pnl.add(text_Show, BorderLayout.CENTER); pnl.add(pnl_1, BorderLayout.SOUTH); pnl_1.setLayout(new BorderLayout()); text = new JTextField(5); text.setEditable(false); text.setBackground(new Color(220, 220, 220)); label = new JLabel(" "); btn_Bk = new JButton("Backspace"); btn_Bk.setForeground(Color.red); btn_CE = new JButton("CE"); btn_CE.setForeground(Color.red); btn_C = new JButton("C"); btn_C.setForeground(Color.red); btn_Bk.addActionListener(this); btn_CE.addActionListener(this); btn_C.addActionListener(this); pnl_1.add(pnl_2, BorderLayout.NORTH); //面板pnl_2装载显示内存数据的方本区和Backspace、CE、C三个按钮 pnl_2.setLayout(new FlowLayout(FlowLayout.RIGHT)); pnl_2.add(text); pnl_2.add(label); pnl_2.add(btn_Bk); pnl_2.add(btn_CE); pnl_2.add(btn_C); pnl_3 = new JPanel(); pnl_1.add(pnl_3, BorderLayout.CENTER); btn = new JButton[10]; //创建表示0~9的数字按钮 for (int i = 0; i < btn.length; i++) { btn[i] = new JButton(Integer.toString(i)); btn[i].setForeground(Color.blue); //按钮上的字体颜色为蓝色 } btn_MC = new JButton("MC"); btn_MC.setForeground(Color.red); btn_MR = new JButton("MR"); btn_MR.setForeground(Color.red); btn_MS = new JButton("MS"); btn_MS.setForeground(Color.red); btn_MAdd = new JButton("M+"); btn_MAdd.setForeground(Color.red); btn_Dot = new JButton("."); btn_Dot.setForeground(Color.blue); btn_AddAndSub = new JButton("+/-"); btn_AddAndSub.setForeground(Color.blue); btn_Add = new JButton("+"); btn_Add.setForeground(Color.red); btn_Sub = new JButton("-"); btn_Sub.setForeground(Color.red); btn_Mul = new JButton("*"); btn_Mul.setForeground(Color.red); btn_Div = new JButton("/"); btn_Div.setForeground(Color.red); btn_Mod = new JButton("%"); btn_Mod.setForeground(Color.blue); btn_Sqrt = new JButton("sqrt"); btn_Sqrt.setForeground(Color.blue); btn_Dao = new JButton("1/x"); btn_Dao.setForeground(Color.blue); btn_Equal = new JButton("="); btn_Equal.setForeground(Color.red); //面板pnl_3装载其余的按钮 pnl_3.setLayout(new GridLayout(4, 6)); //创建一个4行6列的GridLayout布局方式 //将相应的button组件按布局方式加载到面板pnl_3中 //将所有的button组件注册事件侦听 pnl_3.add(btn_MC); btn_MC.addActionListener(this); pnl_3.add(btn[7]); btn[7].addActionListener(this); pnl_3.add(btn[8]); btn[8].addActionListener(this); pnl_3.add(btn[9]); btn[9].addActionListener(this); pnl_3.add(btn_Div); btn_Div.addActionListener(this); pnl_3.add(btn_Sqrt); btn_Sqrt.addActionListener(this); pnl_3.add(btn_MR); btn_MR.addActionListener(this); pnl_3.add(btn[4]); btn[4].addActionListener(this); pnl_3.add(btn[5]); btn[5].addActionListener(this); pnl_3.add(btn[6]); btn[6].addActionListener(this); pnl_3.add(btn_Mul); btn_Mul.addActionListener(this); pnl_3.add(btn_Mod); btn_Mod.addActionListener(this); pnl_3.add(btn_MS); btn_MS.addActionListener(this); pnl_3.add(btn[1]); btn[1].addActionListener(this); pnl_3.add(btn[2]); btn[2].addActionListener(this); pnl_3.add(btn[3]); btn[3].addActionListener(this); pnl_3.add(btn_Sub); btn_Sub.addActionListener(this); pnl_3.add(btn_Dao); btn_Dao.addActionListener(this); pnl_3.add(btn_MAdd); btn_MAdd.addActionListener(this); pnl_3.add(btn[0]); btn[0].addActionListener(this); pnl_3.add(btn_AddAndSub); btn_AddAndSub.addActionListener(this); pnl_3.add(btn_Dot); btn_Dot.addActionListener(this); pnl_3.add(btn_Add); btn_Add.addActionListener(this); pnl_3.add(btn_Equal); btn_Equal.addActionListener(this); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //给JFrame上的关闭按钮设置默认的关闭方式 pack(); //自动调整窗口的大小,适合它所包含的所有组件的布局 this.setVisible(true); //将所有的组件显示出来 } public void actionPerformed(ActionEvent event) { //设置各个按钮行为 boolean sign = false; //判断是否是double型数参与运算,是为true,不是为false Object temp = event.getSource(); try { //如果按下数据按钮,将按下的按钮代表的数据追加到当前文本框字符串之后 for (int i = 0; i <= 9; i++) if (temp == btn[i] && click_Flag == true) text_Show.setText(text_Show.getText() + Integer.toString(i)); //按下"."按钮时,判断当前文本框内字符串中含不含".",如果已含,则不允许再插入"." if (temp == btn_Dot && click_Flag == true) { boolean isInclude = false; if (text_Show.getText().length() == 0) isInclude = true; for (int i = 0; i < text_Show.getText().length(); i++) if ('.' == text_Show.getText().charAt(i)) { isInclude = true; break; } if (isInclude == false) text_Show.setText(text_Show.getText() + "."); } if((temp == btn_Add||temp == btn_Sub||temp == btn_Mul||temp == btn_Div) && click_Flag == true) { // "+"操作 if (temp == btn_Add) { switch (prekey) { case 0: result += Double.parseDouble(text_Show.getText()); break; case 1: result -= Double.parseDouble(text_Show.getText()); break; case 2: result *= Double.parseDouble(text_Show.getText()); break; case 3: if (Double.parseDouble(text_Show.getText()) == 0) { //text_Show="aa"; text_Show.setText("除数不能为零"); click_Flag = false; } else result /= Double.parseDouble(text_Show.getText()); break; default: result = Double.parseDouble(text_Show.getText()); } text_Show.setText(""); prekey = key = 0; } // "-"操作 if (temp == btn_Sub) { switch (prekey) { case 0: result += Double.parseDouble(text_Show.getText()); break; case 1: result -= Double.parseDouble(text_Show.getText()); break; case 2: result *= Double.parseDouble(text_Show.getText()); break; case 3: if (Double.parseDouble(text_Show.getText()) == 0) { text_Show.setText("除数不能为零"); click_Flag = false; } else result /= Double.parseDouble(text_Show.getText()); break; default: } result = Double.parseDouble(text_Show.getText()); text_Show.setText(""); prekey = key = 1; } // "*"操作 if (temp == btn_Mul) { switch (prekey) { case 0: result += Double.parseDouble(text_Show.getText()); break; case 1: result -= Double.parseDouble(text_Show.getText()); break; case 2: result *= Double.parseDouble(text_Show.getText()); break; case 3: if (Double.parseDouble(text_Show.getText()) == 0) { text_Show.setText("除数不能为零"); click_Flag = false; } else result /= Double.parseDouble(text_Show.getText()); break; default: result = Double.parseDouble(text_Show.getText()); } //textAnser = ""; text_Show.setText(""); prekey = key = 2; } if (temp == btn_Div) { switch (prekey) { case 0: // "/"操作 result += Double.parseDouble(text_Show.getText()); break; case 1: result -= Double.parseDouble(text_Show.getText()); break; case 2: result *= Double.parseDouble(text_Show.getText()); break; case 3: if (Double.parseDouble(text_Show.getText()) == 0) { text_Show.setText("除数不能为零"); click_Flag = false; } else result /= Double.parseDouble(text_Show.getText()); break; default: } result = Double.parseDouble(text_Show.getText()); text_Show.setText(""); prekey = key = 3; } } //"="操作 if (temp == btn_Equal && click_Flag == true) { if (prekey == 5) { //如果连续按"=",则连续进行运算 if (key == 0) { result += middle; text_Show.setText(df.format(result)); } if (key == 1) { result -= middle; text_Show.setText(df.format(result)); } if (key == 2) { result *= middle; text_Show.setText(df.format(result)); } if (key == 3) { if (Double.parseDouble(text_Show.getText()) == 0) { text_Show.setText("除数不能为零"); click_Flag = false; } else { result /= middle; text_Show.setText(df.format(result)); } } } else { middle = Double.parseDouble(text_Show.getText()); if (key == 0) { prekey = -1; result += Double.parseDouble(text_Show.getText()); text_Show.setText(df.format(result)); } if (key == 1) { prekey = -1; result -= Double.parseDouble(text_Show.getText()); text_Show.setText(df.format(result)); } if (key == 2) { prekey = -1; result *= Double.parseDouble(text_Show.getText()); text_Show.setText(df.format(result)); } if (key == 3) { prekey = -1; if (Double.parseDouble(text_Show.getText()) == 0) { text_Show.setText("除数不能为零"); click_Flag = false; } else { result /= Double.parseDouble(text_Show.getText()); text_Show.setText(df.format(result)); } } } prekey = 5; } // "%"操作,对第二个操作数除以100 if (temp == btn_Mod && click_Flag == true) { if (result == 0) { String s = text_Show.getText(); text_Show.setText(s); } else { boolean isInclude = false; for (int i = 0; i < text_Show.getText().length(); i++) if ('.' == text_Show.getText().charAt(i)) { isInclude = true; break; } //如果是double数,除100 if (isInclude == true) { double dtemp = Double.parseDouble(text_Show.getText()); dtemp = dtemp / 100.0; text_Show.setText(Double.toString(dtemp)); } else { //如果是int数但能被100整除,则去掉末尾两个零 if (Integer.parseInt(text_Show.getText()) % 100 == 0) { int itemp = Integer.parseInt(text_Show.getText()); itemp /= 100; text_Show.setText(Integer.toString(itemp)); } //如果是int数,但不能被100整除,则按double数处理 else { double dtemp = Double.parseDouble(text_Show .getText()); dtemp = dtemp / 100.0; text_Show.setText(Double.toString(dtemp)); } } } } if (temp == btn_Sqrt && click_Flag == true) { //开根号运算 String s = text_Show.getText(); if (s.charAt(0) == '-') { //判断符号是否符合运算规则 text_Show.setText("负数不能开根号"); click_Flag = false; } else text_Show.setText(Double.toString(java.lang.Math .sqrt(Double.parseDouble(text_Show.getText())))); } //倒数运算 if (temp == btn_Dao && click_Flag == true) { if (text_Show.getText().charAt(0) == '0' && text_Show.getText().length() == 1){//判断此数是否符合进行倒数运算规则 text_Show.setText("零不能求倒数"); click_Flag = false; } else { boolean isDec = true; int i, j, k; String s = Double.toString(1 / Double.parseDouble(text_Show .getText())); for (i = 0; i < s.length(); i++) if (s.charAt(i) == '.') break; for (j = i + 1; j < s.length(); j++) if (s.charAt(j) != '0') { isDec = false; break; } if (isDec == true) { String stemp = ""; for (k = 0; k < i; k++) stemp += s.charAt(k); text_Show.setText(stemp); } else text_Show.setText(s); } } if (temp == btn_AddAndSub && click_Flag == true) {//按下"+/-"按钮时处理 boolean isNumber = true; String s = text_Show.getText(); for (int i = 0; i < s.length(); i++) if (!(s.charAt(i) >= '0' && s.charAt(i) <= '9' || s.charAt(i) == '.' || s.charAt(i) == '-')) { isNumber = false; break; } if (isNumber == true) { if (s.charAt(0) == '-') { //如果当前字符串首字母有'-'号,代表现在是个负数,再按下时,则将首符号去掉 text_Show.setText(""); for (int i = 1; i < s.length(); i++) { char a = s.charAt(i); text_Show.setText(text_Show.getText() + a); } } //如果当前字符串第一个字符不是符号,则添加一个符号在首字母处 else text_Show.setText('-' + s); } } //计算器有关内存操作 //'MC'的操作,将内存清0 if (temp == btn_MC && click_Flag == true) { memory_data = memory_int = 0; text.setText(""); } //'MS'的操作,将当前文本框内容保存入内存,显示'M' if (temp == btn_MS && click_Flag == true) { boolean isInclude = false; text.setText(" M"); for (int i = 0; i < text_Show.getText().length(); i++) if ('.' == text_Show.getText().charAt(i)) { isInclude = true; break; } //如果是double,则存入memory_data(double存储器) if (isInclude == true) { memory_data = Double.parseDouble(text_Show.getText()); memory_int = 0; //保证存储器中存放最新的值 } //如果是int,则存入memory_int(int存储器) else { memory_int = Integer.parseInt(text_Show.getText()); memory_data = 0;//保证存储器中存放最新的值 } } //'MR'的操作,将存储器中的信息输出 if (temp == btn_MR && click_Flag == true) { if (memory_data != 0) text_Show.setText(Double.toString(memory_data)); if (memory_int != 0) text_Show.setText(Integer.toString(memory_int)); } //'M+'的功能,将当前文本框里的数据和存储器中数据相加后,再存入存储器 if (temp == btn_MAdd && click_Flag == true) { boolean isInclude = false; for (int i = 0; i < text_Show.getText().length(); i++) if ('.' == text_Show.getText().charAt(i)) { isInclude = true; break; } if (memory_int != 0) { //存储中是一个int型数 if (isInclude == false) //被加数是一个int型数 memory_int += Integer.parseInt(text_Show.getText()); else { //被加数是一个double型数,则将int存储器中数传入double存储器与当前数相加,int存储器清零 memory_data = memory_int + Double.parseDouble(text_Show.getText()); memory_int = 0; } } else memory_data += Double.parseDouble(text_Show.getText()); } //按下'Backspace'键,利用循环将当前字符串中的最后一个字母删除 if (temp == btn_Bk && click_Flag == true) { String s = text_Show.getText(); text_Show.setText(""); for (int i = 0; i < s.length() - 1; i++) { char a = s.charAt(i); text_Show.setText(text_Show.getText() + a); } } //按下'CE'按钮,将当前文本框内数据清除 if (temp == btn_CE) { text_Show.setText(""); click_Flag = true; } //按下'C'按钮,文本框内数据清除,同时middle,result清0 if (temp == btn_C) { middle = result = 0; text_Show.setText(""); click_Flag = true; } } //输入中如果有操作非法,比如按下两次'+',捕获异常 catch (Exception e) { text_Show.setText("操作非法"); click_Flag = false; } } public static void main(String args[]) { //主函数 new CalculatorDemo(); } }
(3)运行结果如图7-5所示。现在可以运行一个简单的计算,最后的结果如图7-6所示。
图7-5 初始界面
图7-6 显示运算结果界面
源程序解读
(1)CalculatorDemo是构造方法。在此方法中对所有按键进行布局,通过DecimalFormat()方法对double型数据进行精度设置。通过setForeground()可以设置按钮的字体颜色。setDefaultCloseOperation()的作用是设置当用户在此对话框上发起close时默认执行的操作。pack()方法会自动将窗口调整为适合它所包含的所有组件的布局的范围。
(2)actionPerformed方法用于处理事件,实现ActionListener接口。通过event.getSource();获取事件发生的源对象。例如,如果源对象等于btn_Add,则进行加运算处理。
实例48 日历和日期
日期类包括Date、Calendar和GregorianCalendar类,其中Date类提供获取日期和时间的方法,Calendar和GregorianCalendar类是日历类,它们的功能比Date更强,但Calendar是抽象类, GregorianCalendar是它的子类。本实例将演示日历和日期的使用方法。
技术要点
学习日历和日期的技术要点如下:
• Date是Java类库里提供对时间进行处理的类,日期在商业逻辑的应用中占据着很重要的地位。它最简单的构造函数是Date(),它以当前的日期和时间初始化一个Date对象。由于开始设计Date时没有考虑到国际化,所以后来又设计了两个新的类来解决Date类中的问题,一个是Calendar类,一个是DateFormat类。从JDK1.1起,Date中的方法就被废弃掉,全部都被移植到另外一个类Calendar中。
• Calendar类是一个抽象基类,主要完成日期字段之间相互操作的功能,它为一些特定的时间,例如YEAR、MONTH、DAY_OF_MONTH和HOUR等日历字段之间的转换提供了一些方法,并为操作日历字段(如获得下星期的日期)提供了一些方法。Calendar提供了一个类方法getInstance,以获得此类型的一个通用的对象。Calendar 的 getInstance方法返回一个Calendar对象,其日历字段已由当前日期和时间初始化。
• DateFormat提供了很多类方法,以获得基于默认或给定语言环境和多种格式化风格的默认日期/时间Formatter。格式化风格包括FULL、LONG、MEDIUM和SHORT。方法描述中提供了使用这些风格的更多细节和示例。还可帮助进行格式化并分析任何语言环境的日期。java.text.SimpleDateFormat类是JDK目前提供的一个DateFormat子类。它是一个具体类,具有把Date对象格式化为本地字符串,或者通过语义分析把日期或时间字符串转换为Date对象的功能。
实现步骤
(1)新建一个类名为CalendarAndDate.java。
(2)代码如下所示:
package chp07; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; import java.util.GregorianCalendar; public class CalendarAndDate { public static void main(String[] args) { CalendarAndDate.DateExample(); CalendarAndDate.Simple_Date(); CalendarAndDate.date_Format(); CalendarAndDate.Date_Format(); } public static void DateExample() { System.out.println("使用Date类获取系统的当前时间的示例如下所示:"); //得到系统日期/时间 Date date = new Date(); System.out.println("现在的时间为:" + date.getTime() + "\n"); } public static void Simple_Date() { System.out.println("使用SimpleDateFormat类获取系统的当前时间的示例如下所示:"); System.out.println("时间格式EEEE-MMMM-dd-yyyy:"); //创建一个日期格式化,日期的形式为EEEE-MMMM-dd-yyyy SimpleDateFormat bartDateFormat = new SimpleDateFormat( "EEEE-MMMM-dd-yyyy"); Date date = new Date(); System.out.println("当前系统的日期为:" + bartDateFormat.format(date)); System.out.println("时间格式MM-dd-yyyy:"); //创建一个日期格式化,可以解析日期的形式为MM-dd-yyyy SimpleDateFormat bartDateFormat1 = new SimpleDateFormat("MM-dd-yyyy"); //创建一个字符串包含一个文本日期 String dateStringToParse = "09-29-2001"; try { Date date1 = bartDateFormat1.parse(dateStringToParse); System.out.println("MM-dd-yyyy形式的日期:" + date1.getTime() + "\n"); } catch (Exception ex) { System.out.println("日期格式解析错误!!" + ex.getMessage()); } } public static void date_Format() { System.out.println("使用DateFormat类获取系统的当前时间的示例如下所示:"); Date date = new Date(); DateFormat shortDateFormat = DateFormat.getDateTimeInstance( DateFormat.SHORT, DateFormat.SHORT); DateFormat mediumDateFormat = DateFormat.getDateTimeInstance( DateFormat.MEDIUM, DateFormat.MEDIUM); DateFormat longDateFormat = DateFormat.getDateTimeInstance( DateFormat.LONG, DateFormat.LONG); DateFormat fullDateFormat = DateFormat.getDateTimeInstance( DateFormat.FULL, DateFormat.FULL); System.out.println("SHORT模式的日期为:" + shortDateFormat.format(date)); System.out.println("MEDIUM模式的日期为:" + mediumDateFormat.format(date)); System.out.println("LONG模式的日期为:" + longDateFormat.format(date)); System.out.println("FULL模式的日期为:" + fullDateFormat.format(date)); } public static void Date_Format() { System.out.println("使用GregorianCalendar类获取系统的当前时间的示例如下所示"); DateFormat dateFormat = DateFormat.getDateInstance(DateFormat.FULL); //创建一个GregorianCalendar日历对象 GregorianCalendar cal = new GregorianCalendar(); //将日历系统的日期和时间设置给cal cal.setTime(new Date()); System.out.println("GregorianCalendar类的日期: " + dateFormat.format(cal.getTime())); //将DAY_OF_WEEK的值设为MONDAY cal.set(GregorianCalendar.DAY_OF_WEEK, GregorianCalendar.SUNDAY); System.out.println("在本周内,星期日的日期为:" + dateFormat.format(cal.getTime())); int count = 0; System.out.println("符合条件的日期如下:"); while (count <= 5) { //DAY_OF_MONTH的值设为7 cal.add(GregorianCalendar.DAY_OF_MONTH, 7); //判断某月中某日的日期是15号且是星期日 if (cal.get(GregorianCalendar.DAY_OF_MONTH) == 15) { count++; System.out.println(dateFormat.format(cal.getTime())); } } } }
(3)运行结果如图7-7所示。
图7-7 运行结果
源程序解读
(1)DateExample():使用Date类获取系统的当前时间,new Date()表示得到系统日期/时间。
(2)Simple_Date():使用SimpleDateFormat类获取系统的当前时间,new SimpleDateFormat("EEEE-MMMM-dd-yyyy")表示创建一个日期格式化,日期的形式为EEEE-MMMM-dd-yyyy;new SimpleDateFormat ("MM-dd-yyyy")表示创建一个字符串包含一个文本日期的SimpleDateFormat对象,parse()进行日期转换。
(3)date_Format():使用DateFormat类获取系统的当前时间,DateFormat. getDateTimeInstance (DateFormat.SHORT, DateFormat. SHORT)表示获取SHORT模式的日期。
(4).Date_Format():使用GregorianCalendar类获取系统的当前时间,DateFormat. getDateInstance (DateFormat.FULL)表示创建一个GregorianCalendar日历对象;cal.add(GregorianCalendar.DAY_OF_MONTH, 7)的作用是将DAY_OF_MONTH的值设为7。
实例49 Java编制的时钟
本实例是一个用Java实现的简单动态时钟的小程序,主要是以图形和数字两种不同的方式来显示当前的系统时间。
技术要点
实现Java编制的时钟的技术要点如下:
• java.util.Calendar包的使用
绘制时钟图形。
• GregorianCalendar()的用法
实现步骤
(1)新建一个类名为Simple_Clock.java。
(2)代码如下所示:
package chp07; import javax.swing.*; import java.awt.event.ActionListener; import java.awt.event.ActionEvent; import java.awt.*; import java.util.Calendar; import java.util.GregorianCalendar; public class Simple_Clock extends JFrame implements ActionListener { int x, y, x_1, y_1, r, h, ss_x, ss_y, mm_x, mm_y, hh_x, hh_y, ss, mm, hh, old_m, old_h, ang; final double RAD = Math.PI / 180; //度数转换成弧度的比例 //构造函数创建了一个窗体 public Simple_Clock() { super("Java Applet小时钟"); setDefaultCloseOperation(3); Image image = getToolkit().getImage("1.jpg"); setIconImage(image); setSize(250, 250); setBackground(Color.black); setLocation(300, 150); setResizable(true); show(); int delay = 1000; ActionListener draw = new ActionListener() {//创建一个监听事件 public void actionPerformed(ActionEvent evt) { repaint(); } }; //创建一个时间计数器,每一秒触发一次 new Timer(delay, draw).start(); } //实现ActionListener接口必须实现的方法 public void actionPerformed(ActionEvent e) { } public void paint(Graphics g) { //绘制图形 Graphics2D g2D = (Graphics2D) g; //Graphics强制类型转换成Graphics2D Insets insets = getInsets(); //创建JFrame的边界对象 int L = insets.left / 2, T = insets.top/2; //L=左边界的2/1,T=上边界的2/1 h = getSize().height; //返回JFrame的高度 g.setColor(new Color(115, 74, 18)); //设置画笔的颜色为棕色 //画圆 g2D.setStroke(new BasicStroke(5.0f)); //勾画出宽度为4的圆 g.drawOval(L + 40, T + 40, h - 80, h - 80);// r = h / 2 - 40; x_1 = 40 + r - 5 + L; y_1 = 40 + r - 5 - T; ang = 60; //绘制时钟上的12个字 g.setFont(new Font("", Font.BOLD, 14)); g.setColor(new Color(240, 255, 255)); for (int i = 1; i <= 12; i++) { x = (int) ((r + 12) * Math.cos(RAD * ang) + x_1); y = (int) ((r + 12) * Math.sin(RAD * ang) + y_1); g.drawString("" + i, x, h - y); ang -= 30; } //获得现在的时间 Calendar now = new GregorianCalendar(); //创建Calendar对象now int hour = now.get(Calendar.HOUR_OF_DAY); //取得当前的小时部分 int minute = now.get(Calendar.MINUTE); //取得当前时间的分钟部分 int second = now.get(Calendar.SECOND); //取得当前时间的秒 String st; if (hour < 10) st = "0" + hour; else st = "" + hour; if (minute < 10) st += ":0" + minute; else st += ":" + minute; if (second < 10) st += ":0" + second; else st += ":" + second; //在窗体上显示时间格式,如00:00:00 g.setColor(Color.pink); g.fillRect(L, T, 60, 30); //画出宽为60像素,高为30像素的矩形,颜色为粉色 g.setColor(Color.yellow); g.drawString(st, L + 2, T + 26); //将上面组合的字符串st画出,颜色为黄色 //计算时间与度数的关系 ss = 90 - second * 6; mm = 90 - minute * 6; hh = 90 - hour * 30 - minute / 2; x_1 = r + 40 + L; y_1 = r + 40 + T; g2D.setStroke(new BasicStroke(1.0f)); //设置秒针的宽度 if (ss_x > 0) { //擦除秒针 g.setColor(getBackground()); g.drawLine(x_1, y_1, ss_x, h - ss_y); } else { old_m = mm; old_h = hh; } //绘制秒针 x = (int) (r * 0.9 * Math.cos(RAD * ss)) + x_1; y = (int) (r * 0.9 * Math.sin(RAD * ss)) + y_1 - 2 * T; g.setColor(new Color(255, 99, 71)); g.drawLine(x_1, y_1, x, h - y); ss_x = x; ss_y = y; g2D.setStroke(new BasicStroke(2.0f)); //设置分针的宽度 //擦除分针 if (old_m != mm) { g.setColor(getBackground()); g.drawLine(x_1, y_1, mm_x, h - mm_y); } //绘制分针 x = (int) (r * 0.7 * Math.cos(RAD * mm)) + x_1; y = (int) (r * 0.7 * Math.sin(RAD * mm)) + y_1 - 2 * T; g.setColor(new Color(138, 43, 226)); g.drawLine(x_1, y_1, x, h - y); mm_x = x; mm_y = y; old_m = mm; g2D.setStroke(new BasicStroke(3.0f)); //设置时针的宽度 //擦除时针 if (old_h != hh) { g.setColor(getBackground()); g.drawLine(x_1, y_1, hh_x, h - hh_y); } //绘制时针 x = (int) (r * 0.5 * Math.cos(RAD * hh)) + x_1; y = (int) (r * 0.5 * Math.sin(RAD * hh)) + y_1 - 2 * T; g.setColor(new Color(107, 142, 35)); g.drawLine(x_1, y_1, x, h - y); hh_x = x; hh_y = y; old_h = hh; } public static void main(String[] args) { Simple_Clock cd = new Simple_Clock(); } }
(3)运行结果如图7-8所示。从中可以看到这个钟表和当前系统的时间是一致的。
图7-8 运行结果
源程序解读
(1)GregorianCalendar的用法:
Calendar now=new GregorianCalendar();
(2)绘制图形Graphics2D g2D = (Graphics2D) g;,Graphics强制类型转换成Graphics2D。
(3)显示时钟,风格可以自定。
(4)获取系统的时间。
int hour=now.get(Calendar.HOUR_OF_DAY):取得当前的小时部分。
int minute=now.get(Calendar.MINUTE):取得当前时间的分钟部分。
int second=now.get(Calendar.SECOND):取得当前时间的秒。
实例50 简单的日历
本实例使用Swing和AWT实现一个图形化的日历,通过java.util.Date和java.text. DateFormat等日期类来实现可以查看星期、日期、月份和年份等信息功能。
技术要点
实现图形日历的技术要点如下:
• JTable的使用方法。
• AbstractTableModel抽象类的作用是负责管理侦听器。
• 使用下拉框JComboBox存放年份和月份,供用户选择。
• 通过addActionListener方法实现对月份下拉框的监听。
• 使用java.util.GregorianCalendar类可以很方便地计算日期、星期和闰月。
• 使用java.text.DateFormat类可以实现格式化并分析任何语言环境的日期。
实现步骤
(1)新建一个类名为CalendarDemo.java。
(2)代码如下所示:
package chp07; import java.awt.*; import java.awt.event.*; import java.util.*; import javax.swing.*; import javax.swing.event.*; import javax.swing.table.*; public class CalendarDemo extends JApplet { //定义星期天到星期六 public static final String SUN = "周日"; public static final String MON = "周一"; public static final String TUE = "周二"; public static final String WED = "周三"; public static final String THU = "周四"; public static final String FRI = "周五"; public static final String SAT = "周六"; public static final Color background = new Color(240, 255, 255); //设置背景颜色 public static final Color foreground = new Color(128, 138, 135); //设置前景颜色 public static final Color H_Background = new Color(240, 255, 240); public static final Color H_Foreground = new Color(61, 89, 171); //设置题头星期的背景颜色和前景颜色 public static final Color selectedBackground = new Color(245, 222, 179); //设置被选中的日期的背景颜色和前景颜色 public static final Color selectedForeground = new Color(255, 97, 0); private JPanel pan; private JLabel year_Label; //年 private JSpinner year_Spinner; //年调控 private JLabel month_Label; //月 private JComboBox month_ComboBox; //月下拉框 private JTable table; //用来显示日期的table private AbstractTableModel model; private Calendar calendar; public CalendarDemo() { //构造方法初始化panel pan = (JPanel) getContentPane(); } public void init() { //初始化,对所有空间进行布局 pan.setLayout(new BorderLayout()); //使用border布局管理器 calendar = Calendar.getInstance(); year_Label = new JLabel("年份: "); year_Spinner = new JSpinner(); year_Spinner.setEditor(new JSpinner.NumberEditor(year_Spinner, "0000")); year_Spinner.setValue(new Integer(calendar.get(Calendar.YEAR))); //增加监听,监听年份的改变 year_Spinner.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent changeEvent) { int day = calendar.get(Calendar.DAY_OF_MONTH); calendar.set(Calendar.DAY_OF_MONTH, 1); calendar.set(Calendar.YEAR, ((Integer) year_Spinner.getValue()) .intValue()); int maxDay = calendar.getActualMaximum(Calendar.DAY_OF_MONTH); calendar .set(Calendar.DAY_OF_MONTH, day > maxDay ? maxDay : day); change_View(); } }); JPanel Y_Mpanel = new JPanel(); pan.add(Y_Mpanel, BorderLayout.NORTH); Y_Mpanel.setLayout(new BorderLayout()); Y_Mpanel.add(new JPanel(), BorderLayout.CENTER); JPanel Y_Panel = new JPanel(); Y_Mpanel.add(Y_Panel, BorderLayout.WEST); Y_Panel.setLayout(new BorderLayout()); Y_Panel.add(year_Label, BorderLayout.WEST); Y_Panel.add(year_Spinner, BorderLayout.CENTER); month_Label = new JLabel("月份: "); month_ComboBox = new JComboBox(); //向月份下拉框中增加内容 for (int i = 1; i <= 12; i++) { month_ComboBox.addItem(" " + new Integer(i) + " "); } month_ComboBox.setSelectedIndex(calendar.get(Calendar.MONTH)); month_ComboBox.addActionListener(new ActionListener() { //监听月份的改变 public void actionPerformed(ActionEvent actionEvent) { int day = calendar.get(Calendar.DAY_OF_MONTH); calendar.set(Calendar.DAY_OF_MONTH, 1); calendar.set(Calendar.MONTH, month_ComboBox.getSelectedIndex()); int maxDay = calendar.getActualMaximum(Calendar.DAY_OF_MONTH); calendar .set(Calendar.DAY_OF_MONTH, day > maxDay ? maxDay : day); change_View(); } }); JPanel M_Panel = new JPanel(); Y_Mpanel.add(M_Panel, BorderLayout.EAST); M_Panel.setLayout(new BorderLayout()); M_Panel.add(month_Label, BorderLayout.WEST); M_Panel.add(month_ComboBox, BorderLayout.CENTER); model = new AbstractTableModel() { //创建AbstractTableModel对象, //必须实现getRowCount、getColumnCount和getValueAt这三个方法 //设置行7 public int getRowCount() { //返回表格行的个数 return 7; } //设置列7 public int getColumnCount() { //返回表格列的个数 return 7; } public Object getValueAt(int row, int column) { //返回row和column之间的单元格值 if (row == 0) { return getHeader(column); } row--; Calendar calendar = (Calendar) CalendarDemo.this.calendar .clone(); calendar.set(Calendar.DAY_OF_MONTH, 1); int dayCount = calendar.getActualMaximum(Calendar.DAY_OF_MONTH); int moreDayCount = calendar.get(Calendar.DAY_OF_WEEK) - 1; int index = row * 7 + column; int dayIndex = index - moreDayCount + 1; if (index < moreDayCount || dayIndex > dayCount) { return null; } else { return new Integer(dayIndex); } } }; table = new CalendarTable(model, calendar); table.setRowHeight(25); //设置每个cell可以被选中 table.setCellSelectionEnabled(true); table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); table.setDefaultRenderer(table.getColumnClass(0), new TableCellRenderer() { public Component getTableCellRendererComponent( JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { String text = (value == null) ? "" : value.toString(); JLabel cell = new JLabel(text); cell.setOpaque(true); if (row == 0) { cell.setForeground(H_Foreground); cell.setBackground(H_Background); } else { if (isSelected) { cell.setForeground(selectedForeground); cell.setBackground(selectedBackground); } else { cell.setForeground(foreground); cell.setBackground(background); } } return cell; } }); change_View(); pan.add(table); } public static String getHeader(int index) { switch (index) { case 0: return SUN; case 1: return MON; case 2: return TUE; case 3: return WED; case 4: return THU; case 5: return FRI; case 6: return SAT; default: return null; } } public void change_View() { model.fireTableDataChanged(); //对表格中的单元格进行侦听 table.setRowSelectionInterval(calendar.get(Calendar.WEEK_OF_MONTH), //选择两参数间的行 calendar.get(Calendar.WEEK_OF_MONTH)); table.setColumnSelectionInterval( calendar.get(Calendar.DAY_OF_WEEK) - 1, calendar .get(Calendar.DAY_OF_WEEK) - 1);//选择两参数间的列 } public static class CalendarTable extends JTable { //设置日历的table private Calendar calendar; public CalendarTable(TableModel model, Calendar calendar) { super(model); this.calendar = calendar; } public void changeSelection(int row, int column, boolean toggle, boolean extend) { super.changeSelection(row, column, toggle, extend); if (row == 0) { return; } Object obj = getValueAt(row, column); //获取row行,column列间的值 if (obj != null) { calendar.set(Calendar.DAY_OF_MONTH,((Integer)obj).intValue()); //将Calendar.DAY_OF_MONTH的值设置为int类型的数值 } } } public static void main(String[] args) { //让applet作为一个可执行的程序来运行 JFrame frame = new JFrame("Calendar Application"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); CalendarDemo CalendarDemo = new CalendarDemo(); CalendarDemo.init(); frame.getContentPane().add(CalendarDemo); frame.setVisible(true); } }
(3)运行结果如图7-9所示。当单击某一天时,背景色就会改变成金黄色。
图7-9 运行结果
源程序解读
1. CalendarDemo类
(1)init()的主要作用是初始化Applet,对所有组件进行初始化和布局。创建一个JSpinner对象,JSpinner具有一个负责显示和潜在地更改当前元素或者模型value的唯一子组件,被称为editor。此编辑器由JSpinner的构造方法创建,并且可通过editor属性进行更改。JSpinner的编辑器通过侦听ChangeEvent而与模型保持同步。如果用户更改了editor所显示的值,则有可能model的值与editor的值不一样。
JSpinner让用户从一个有序序列中选择一个数字或者一个对象值的单行输入字段。Spinner通常提供一对带小箭头的按钮以便逐步遍历序列元素。键盘的向上/向下方向键也可循环遍历元素。也允许用户在Spinner中直接输入合法值。JSpinner常用的主要方法如下。
• void setEditor(JComponent editor):更改显示SpinnerModel当前值的JComponent。
• void setValue(Object value):更改模型的当前值,通常此值是editor所显示的值。
• void addChangeListener(ChangeListener listener):为每次发生模型更改时要通知的列表添加侦听器。
(2)stateChanged()的主要作用是对年份的更改进行监听。Calendar类中的get()方法返回给定日历字段的值。Calendar类中的set()方法将给定的日历字段设置为给定值。
(3)change_View()的主要作用是对表格中的单元格进行侦听。
2. CalendarTable类
CalendarTable继承了JTable,JTable用来显示和编辑规则的二维单元表。
(1)changeSelection()也重载了父类void changeSelection(int rowIndex,int columnIndex, boolean toggle, boolean extend):根据标志toggle和extend的状态,更新表的选择模型。其中, rowIndex表示影响row的选择;columnIndex表示影响column的选择。选择模型toggle和extend的含义如下。
• toggle:false,extend:false。清除以前的选择并确保选定新的单元格。
• toggle:false,extend:true。将以前的选择从定位点扩展到指定的单元格,清除所有其他选择。
• toggle:true,extend:false。如果指定的单元格是选定的,则取消选定它。如果它不是选定的,则选定它。
• toggle:true,extend:true。保持当前的选择状态,但将定位点索引移动到指定位置。
(2)JTable用来显示和编辑规则的二维单元表。它的常用方法如下。
• Object getValueAt(int row, int column):返回row和column位置的单元格值。
• void setRowSelectionInterval(int index0,int index1):选择从index0到index1(包含)之间的行。
• void setColumnSelectionInterval(int index0,int index1):选择从index0到index1(包含)之间的列。
• void setRowHeight(int row, int rowHeight):将row的高度设置为rowHeight、重新验证并重新绘制它。
• void setCellSelectionEnabled(boolean cellSelectionEnabled):设置此表是否允许同时存在行选择和列选择。
• void setSelectionMode(int selectionMode):将表的选择模式设置为只允许单个选择、单个连续单元格选择或多个连续选择。
• void setDefaultRenderer(Class<?> columnClass, TableCellRenderer renderer):如果在TableColumn中未设置渲染器,则设置要使用的默认单元格渲染器。
(3)AbstractTableModel抽象类为TableModel接口中的大多数方法提供默认实现。它负责管理侦听器,并为生成TableModelEvents以及将其调度到侦听器提供方便。要创建一个具体的TableModel作为AbstractTableModel的子类,只需提供对以下4个方法的实现。
• public int getRowCount():返回表格行的个数。
• public int getColumnCount():返回表格列的个数。
• public Object getValueAt(int row, int column):返回row和column之间的单元格值。
• void fireTableChanged(TableModelEvente):将给定的通知事件转发到所有将自身注册为此表模型的侦听器的TableModelListeners。
(4)getHeader()的主要作用是设置日历的头列。
实例51 内存管理
由于Java提供了垃圾内存收集机制,所以不会像C语言那样,需要程序员手动对内存进行管理。但是还是需要Java程序员了解虚拟机中的内存使用情况,以便能更好地运行程序。这就需要通过Runtime对象来了解内存的相关信息。
技术要点
运用Runtime类来获取相应的内存信息的技术要点如下:
• 利用Runtime对象的totalMemory()来获取虚拟机的总内存。
• 利用Runtime对象的freeMemory()来获取虚拟机中空闲内存的大小。
• 利用Runtime对象的gc()来启动垃圾收集线程。
实现步骤
(1)新建一个类名为Runtime_Memory.java。
(2)代码如下所示:
package chp07; public class Runtime_Memory { public static void main(String args[]) { try { Runtime run = Runtime.getRuntime();//获取与当前运行类相关联的runtime实例 System.out.println("内存可用空间:" + run.totalMemory()); System.out.println("未创建数组时剩余内存空间:" + run.freeMemory()); int base[] = new int[10240]; //申请空间 System.out.println("创建数组后剩余内存空间:" + run.freeMemory()); run.gc();//启动垃圾收集线程 Thread.sleep(1000); System.out.println("启动垃圾收集之后剩余空间:" + run.freeMemory()); System.out.println("======为数组分配空间====="); for (int i = 0; i < 10240; ++i) base[i] = i + 1; Thread.sleep(2000); System.out.println("分配空间之后剩余空间:" + run.freeMemory()); run.gc();//启动垃圾收集线程 Thread.sleep(2000); System.out.println("启动垃圾收集之后剩余空间:" + run.freeMemory()); } catch (InterruptedException el) { el.printStackTrace(); } } }
(3)运行结果如图7-10所示。
图7-10 运行结果
源程序解读
(1)在上面的实例中,首先通过run.totalMemory()语句来获取当前机器中的虚拟机的总内存,接着通过run.freeMemory()语句来获取当前虚拟机的空闲内存。然后创建一个int型的数组,来申请一些内存,却不使用它,再查看剩余内存大小,并利用run.gc();启动垃圾收集机制,再次查看内存,重复上述动作。
(2)在上面每次启动gc()的时候,都会使用线程休眠:Thread.sleep(2000),这主要是因为垃圾收集线程的优先级很低,若不令当前正在运行的线程休眠,垃圾收集线程是没有机会执行的。
实例52 利用currentTimeMillis()计算程序执行的时间
System类是系统中最常用的类,它有3个非常有用的静态成员:out、in和err,分别表示标准的输出流、输入流和错误输出流。除此之外,System类还定义了一些静态方法,供程序与系统的交互,currentTimeMillis()方法就是其中之一。本实例将演示如何利用currentTimeMillis()来计算程序的运行时间。
技术要点
currentTimeMillis()是以毫秒为单位获取计算机上的时间,它的返回值是一个long型,记录的是当前时间与1970年1月1日0时之间的时间差。可以将它转换成能够阅读的时间,也可以用它来记录一个程序执行的时间。
实现步骤
(1)新建一个类名为System-Milis.java。
(2)代码如下所示:
package chp07; public class System_Milis { public static void main(String args[]) { try { long start = System.currentTimeMillis(); System.out.println("程序开始执行的时间为:"+start); Thread.sleep(2000); long end = System.currentTimeMillis(); System.out.println("程序运行结束的时间为: "+end); System.out.println("程序执行时间为:" + (end - start) + "毫秒"); } catch (InterruptedException el) { el.printStackTrace(); } } }
(3)运行结果如图7-11所示。
图7-11 运行结果
源程序解读
在本程序中首先用System.currentTimeMillis()获得程序的开始运行时间,然后令主线程休眠2000毫秒。接着同样用System.currentTimeMillis()获得程序的结束运行时间。最后用开始时间减去结束时间得到的结果就是本程序所运行的时间。
实例53 利用exit()退出虚拟机
一般情况下,当main()方法执行完毕后,程序会自动退出,同时虚拟机也会自动退出。但不是每个程序都这样的,例如用swing编写GUI界面程序时,若用户不对窗口事件做处理,当关闭窗口时,JVM虚拟机并不会退出。如启动了一个可以插放音乐的小程序,当关闭窗口时,音乐却还在插放。本实例将演示如何利用System类的exit()强制虚拟机退出。
技术要点
public static void exit(int status)的主要作用是终止当前正在运行的Java虚拟机。其中参数status表示退出的状态,根据惯例,非零的状态码表示异常终止,即exit(0)表示正常退出;exit(1)表示异常错误使得退出调用。System.exit(n)实际上等效于调用Runtime.getRuntime(). exit(n)。
实现步骤
(1)新建一个类名为JVM_Exit.java。
(2)代码如下所示:
package chp07; public class JVM_Exit extends Thread { public static void main(String args[]) { JVM_Exit je = new JVM_Exit(); Thread th = Thread.currentThread();//获取主线程 je.start(); try { th.sleep(1000);//令线程暂停运行 } catch (InterruptedException el) { el.printStackTrace(); } System.out.println("主线程运行结束"); } public void run() { //线程类的抽象方法 System.out.println("正在运行子线程"); System.exit(0); //强制JVM退出 } }
(3)运行结果如图7-12所示。
图7-12 运行结果
源程序解读
本程序通过Thread th = Thread.currentThread()语句获取主线程,然后启动子线程je,由于子线程的优先级低于主线程的优先级,所以在主线程没有停止运行前,子线程是不被运行。为了能让子线程je运行,通过th.sleep(1000);令线程暂停运行。这时便会执行run(),在输出打印语句之后,JVM就强制退出了,所以整个程序也就结束了。那么主线程也被强制终止了,所以主线程中的输出语句也就没有机会执行了。
实例54 获取和设置环境属性
在编程的过程中,有时需要知道应用程运行的环境属性,System类提供了一些方法可用于获取及设置环境属性。本实例将演示如何利用System类对环境属性进行操作。
技术要点
System类提供的getPropertie()和setPropertie()可以获取和设置环境属性。可用于获取和设置的环境属性如下所示:
• java.version:Java运行环境的版本。
• java.vendor:Java运行环境的生产商。
• java.vendor.url:生产商的网址。
• java.home:Java的安装路径。
• java.vm.specification.version:虚拟机所遵循的规范的版本。
• java.vm.specification.vendor:虚拟机规范的生产商。
• java.vm.specification.name:虚拟机规范的名称。
• java.vm.version:虚拟机实现的版本。
• java.vm.vendor:虚拟机实现的生产商。
• java.vm.name:虚拟机实现的名称。
• java.specification.version:运行环境所遵循的规范的版本。
• java.specification.vendor:运行环境规范的生产商。
• java.specification.name:运行环境规范的名称。
• java.class.version:Java类格式化的版本号。
• java.class.path:类所在的路径。
• java.library.path:装载类库时所搜索的路径。
• java.io.tmpdir:默认的临时文件路径。
• java.compiler:JIT编译器所使用的名字。
• java.ext.dirs:扩展目录所在的路径。
• os.name:操作系统的名称。
• os.arch:操作系统的架构。
• os.version:操作系统的版本。
• file.separator:文件分隔符(UNIX下是“/”)。
• path.separator:路径分隔符(UNIX下是“:”)。
• line.separator:行分隔符(UNIX下是“\n”)。
• user.name:用户的账户名称。
• user.home:用户的home路径(即UNIX和Linux下当前用户的home所在目录)。
• user.dir:当前用户工作目录。
实现步骤
(1)新建一个类名为Properties.java。
(2)代码如下所示:
package chp07; public class Properties { public static void main(String args[]) { System.out.println("Java运行环境的版本:" + System.getProperty("java.version")); System.out.println("Java运行环境的生产商:" + System.getProperty("java.vendor")); System.out.println("Java的安装路径:" + System.getProperty("java.home")); System.out.println("虚拟机实现的版本:" + System.getProperty("java.vm.version")); System.out.println("虚拟机实现的生产商:" + System.getProperty("java.vm.vendor")); System.out.println("默认的临时文件路径:" + System.getProperty("java.io.tmpdir")); System.out.println("用户的账户名称:" + System.getProperty("user.name")); System.out.println("当前用户工作目录:" + System.getProperty("user.dir")); System.out.println("用户的home路径:" + System.getProperty("user.home")); System.out.println("操作系统的名称:" + System.getProperty("os.name")); System.out.println("操作系统的版本:" + System.getProperty("os.version")); System.out.println("操作系统的架构:" + System.getProperty("os.arch")); System.out.println("运行环境规范的名称:" + System.getProperty("java.specification.name")); System.out.println("Java类格式化的版本号:" + System.getProperty("java.class.version")); System.out.println("类所在的路径:" + System.getProperty("java.class.path")); } }
(3)运行结果如图7-13所示。
图7-13 运行结果
实例55 利用换底公式求任意对数值
在Math类中,提供了常用的数学函数供开发者使用,这些数学函数包括随机函数、三角函数、指数函数和取整函数等。另外,Math类还提供了两个静态成员常量:E和PI。本实例将演示如何利用Math类的指数函数求任意对数值。换底公式:lognm=logem/logen。
技术要点
Math类中提供了8个与指数和对数相关的函数,如下所示:
• static double cbrt(double a):返回a的立方根。
• static double exp(double a):返回e的a次方,e是自然数。
• static double expm1(double x):返回ex-1。
• static double log(double a):返回以e为底,a的对数值。
• static double log10(double a):返回以10为底,a的对数值。
• static double log1p(double x):返回x+1的自然对数值。
• static double pow(double a, double b):返回ab。
• static double sqrt(double a):返回a的平方根。
实现步骤
(1)新建一个类名为Math_Log.java。
(2)代码如下所示:
package chp07; public class Math_Log { public static void main(String args[]) { double n = 5; double m = 23.0; double value; value = Math.log(m) / Math.log(n); System.out.println("以5为底23的对数为:" + value); } }
(3)运行结果如图7-14所示。
图7-14 运行结果
实例56 使用取整函数
Math类中还提供了多种取整函数,本实例将演示如何利用Math类的取整函数求任意实数的整数。
技术要点
Math类中提供了5个与取整相关的函数,如下所示:
• static double ceil(double a):天花板函数,返回大于等于a的最小整数(但是以浮点数形式存储)。
• static double floor(double a):地板函数,返回小于等于a的最大整数(但是以浮点数形式存储)。
• static double rint(double a):四舍五入函数,返回与a的值最相近的整数(但是以浮点数形式存储)。
• static long round(double a):四舍五入函数,返回与a的值最相近的长整型数。
• static int round(float a):四舍五入函数,返回与a的值最相近的整型数。
实现步骤
(1)新建一个类名为Math_Round.java。
(2)代码如下所示:
package chp07; import java.util.Scanner; public class Math_Round { public static void main(String args[]) { double num; Scanner in = new Scanner(System.in); System.out.print("请输入一个浮点数:"); num = in.nextDouble(); double cnum = Math.ceil(num); System.out.println("大于" + num + "的最小数:" + cnum); double fnum = Math.floor(num); System.out.println("小于" + num + "的最大数:" + fnum); double rnum = Math.rint(num); System.out.println(num + "四舍五入得到浮点数:" + rnum); long lnum = Math.round(num); System.out.println(num + "四舍五入得到长整数:" + lnum); } }
(3)运行结果如图7-15所示。
图7-15 运行结果
实例57 利用GregorianCalendar输出日历
Date和Calendar是Java类库里提供对时间进行处理的类, Date类就是与日期相关的类。java.util包中的Date类的主要作用就是获取当前时间,可是由于自身设计上的问题,在使用的过程中此类中的方法遭到众多不好的评论。因此,Date类中的方法从JDK 1.1开始就被作废,并全部转入另外一个类Calendar。Calendar有一个子类GregorianCalendar,GregorianCalendar是Calendar的一个具体子类,提供了世界上大多数国家使用的标准日历系统。本实例将演示如何利用GregorianCalendar类实现一个万年历。
技术要点
Calendar的getInstance()方法返回的就是GregorianCalendar类型对象,该对象不仅拥有Calendar的属性和方法,还有自己的两个属性:AD和BC,分别表示公元前和公元后。GregorianCalendar还提供了若干构造方法,如下所示:
• GregorianCalendar():用机器时间构造一个新对象。
• GregorianCalendar(int year,int month,int dayOfMonth,int hourOfDay,int minute):用指定的年、月、日、时、分构造一个新对象。
• GregorianCalendar(int year, int month, int dayOfMonth, int hourOfDay, int minute, int second):用指定的年、月、日、时、分、秒构造一个新对象。
• GregorianCalendar(Locale aLocale):用aLocale指定的地域、默认的时区和当前时间构造一个新对象。
• GregorianCalendar(TimeZone zone):用zone指定的时区、默认的地域和当前的时间构造一个新对象。
• GregorianCalendar(TimeZone zone,Locale aLocale):用zone指定的时区、aLocale指定的地域和当前时间构造一个新对象。
实现步骤
(1)新建一个类名为CalendarDemo.java。
(2)代码如下所示:
package chp07; import java.util.Calendar; import java.util.GregorianCalendar; import java.util.Scanner; public class Gregorian_Calendar { static final String week[] = { "星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六" }; public static void main(String[] args) { int i; int year = 2009; int month = 10; System.out.println(year + "年" + month + "月的日历如下所示:\n"); GregorianCalendar cal = new GregorianCalendar(year, month - 1, 1); //以指定的年、月、该月的第一天来创建对象 int totalDays = cal.getActualMaximum(Calendar.DAY_OF_MONTH); //获取这个月的天数 int startDay = cal.get(Calendar.DAY_OF_WEEK) - 1; //获取这个月的第一天是星期几 for (i = 0; i < week.length; i++) System.out.print(week[i] + " "); //输出1周七天的星期 System.out.println(); for (i = 0; i < startDay; i++) System.out.print(" "); //输出第一天之前的空格 for (int day = 1; day <= totalDays; day++) { if (day <= 10) { //用于输出控制 System.out.print(day + " "); //依次输出每一天 } else { System.out.print(day + " "); } i++; if (i == 7) { //每个星期输出完,换行 System.out.println(); i = 0; } } } }
(3)运行结果如图7-16所示。
图7-16 运行结果
源程序解读
(1)在程序中要注意月份的设置,如GregorianCalendar al=new GregorianCalendar (year, month-1,1),GregorianCalendar是从0开始,也就是说0表示的是第一个月,这个和数组的下标有点类似。而人类的思维则是1表示的是第一个月,所以月份应该减1。
(2)在利用get(Calendar.DAY_OF_WEEK)求某一天是星期几的时候,该方法的返回值的最小值是1。而按照人类的理解,星期日应该用0表示,所以在Calendar中,JANUARY的值为0,SUNDAY的值为1。所以在使用Calendar的时候,一定要注意。
实例58 Formatter类的简单使用
Java语言的格式化输出在很大程度上受到C语言printf的启发。虽然一些格式字符串与C类似,但已进行了某些定制,以适应Java语言,并且利用了其中一些特性。Formatter类就是一个格式与C类似却又适应Java的printf 风格的格式字符串的解释程序。此类提供了对布局对齐和排列的支持,以及对数值、字符串和日期/时间数据的常规格式和特定于语言环境的输出的支持。
技术要点
若想使用Formatter类,那么就首先要创建一个对象,它提供的5个常用的构造方法如下:
• Formatter():构造一个新对象。
• Formatter(Appendable a):用指定目标构造一个新对象。
• Formatter(File file):用指定的file构造一个新对象。
• Formatter(Locale l):用指定的Local创建一个新对象。
• Formatter(OutputStream os):用指定的输出流创建一个新对象。
创建完Formatter对象,接下来就需要使用类中的方法。Formatter类提供的方法如下:
• void close():关闭对象的存储区,如果目标区是Closeable接口的派生类,该类的close方法同时被调用。
• void flush():清空缓冲区,适用于与文件绑定的对象。
• Formatter format(Locale l,String format,Object args):根据format中的格式串,输出args中的数据到Local指定的场所,返回调用的对象。
• Formatter format(String format,Object args):根据format中的格式串,格式化args中的数据,返回调用的对象。
• Locale locale():返回创建本对象时所用的Locale对象。
• Appendable out():返回本对象的输出目标。
实现步骤
(1)新建一个类名为Java_Formatter.java。
(2)代码如下所示:
package chp07; import java.util.Formatter; import java.util.Locale; public class Java_Formatter { public static void main(String[] args) { Object[] ob = new Object[2]; ob[0] = Integer.valueOf(12); ob[1] = Integer.valueOf(126); System.out.println("第一种输出方式:"); Formatter fmt = new Formatter(System.out); //以标准输出设备为目标,创建对象 fmt.format("直接输出,每个输出项占10个字符位:%10d%10d\n\n", ob); //格式化输出数据,并输出到标准输出设备 System.out.println("第二种输出方式:"); StringBuffer buf = new StringBuffer(); fmt = new Formatter(buf); //以指定的字符串为目标,创建对象 fmt.format("输出到指定的缓冲区,每个输出项占5个字符位:%5d%5d\n\n", ob); //格式化输出数据,输出到buf中 System.out.print(buf); //再从buf中输出到屏幕 System.out.println("第三种输出方式:"); fmt = new Formatter(); //以默认的存储区为目标,创建对象 Object[] ob1 = new Object[2]; ob1[0] = Double.valueOf(129.6355666); ob1[1] = Double.valueOf(12.1258989); fmt.format("输出到自带存储区,每个输出项占8个字符位:%8.2f%8.2f", ob1); //格式化输出数据,输出到自己的存储区 System.out.print(fmt); //再从对象的存储区中输出到屏幕 } }
(3)运行结果如图7-17所示。
图7-17 运行结果
源程序解读
(1)第一种输出方式是有指定的输出流构造一个Formatter对象,然后根据format中的格式串将Object数组中的内容输出。其中d表示是int型数据,%5表示数据与数据间的间距为5个字符的宽度。
(2)第二种输出方式是以指定的字符串为目标,创建对象。Formatter对象先将数据输出到指定的缓冲区,每个输出项占5个字符宽度。
(3)第三种输出方式是以默认的存储区为目标,创建对象。格式化输出数据,输出到自己的存储区。其中f表示是浮点数类型数据。
实例59 Pattern类的使用
从JDK1.4开始。Java提供了一个java.util.regex包。该包用于正则表达式对字符进行匹配,它共包括两个类,其中的一个类是Pattern类,Pattern类中存储了一个经过编译的正则表达式,它也提供了简单的正则表达式匹配功能。本实例将演示如何利用Pattern类进行字符串的分割。
技术要点
Pattern类是一个最终类,除了继承Object的构造方法,它没有提供任何带参的构造方法。其常用的方法如下所示:
• static Pattern compile(String regex):将给定的正则表达式编译并创建一个新的Pattern类返回。
• static Pattern compile(String regex,int flags):同上,但增加flags参数的指定,可选的flags参数包括CASE_INSENSITIVE、MULTILINE、OTALL、UNICODE_CASE以及CANON_EQ。
• int flags():返回当前Pattern的匹配flags参数。
• Matcher matcher(CharSequence input):生成一个给定命名的Matcher对象。
• static boolean matches(String regex,CharSequence input):编译给定的正则表达式,并且对输入的字符串以该正则表达式为模开始匹配,该方法适合于该正则表达式只会使用一次的情况,因为这种情况下,并不需要生成一个Matcher对象。
• String pattern():返回Pattern对象所编译的正则表达式。
• String[] split(CharSequence input):将目标字符串按照Pattern里所包含的正则表达式为模进行分割。
• String[] split(CharSequence input,int limit):作用同上,增加参数limit目的在于要指定分割的段数。例如,将limit设为3,那么目标字符串将根据正则表达式分割成三段。
实现步骤
(1)新建一个类名为Java_Pattern.java。
(2)代码如下所示:
package chp07; import java.util.regex.Pattern; public class Java_Pattern { static String str = "D:/myworkspace/China/src/chp07/Java_Pattern.java" + "/这是Java_Pattern.java文件所在工作区的路径/文件名称:Java_Pattern.java"; public static void main(String[] args) { Pattern pat = Pattern.compile("[/]+");//生成一个Pattern,同时编译一个正则表达式 String[] rs = pat.split(str); //用Pattern的split()方法把字符串按"/"分割 for (int i = 0; i < rs.length; i++) //将分割得到的结果输出 System.out.println(rs[i]); } }
(3)运行结果如图7-18所示。
图7-18 运行结果
实例60 匹配方法的使用
在上个实例中提到java.util.regex包,该包用于正则表达式对字符进行匹配,它共包括两个类,其中的一个类是Pattern类,那么另一个类则是Matcher类。本实例将演示如何利用Matcher类进行匹配查找操作。
技术要点
Matcher类的使用需Pattern类配合,由于必须用Pattern对象调用其matcher()来创建Matcher对象,所以Matcher类也没有构造方法。一旦Matcher对象创建完毕,就可以使用该对象进行匹配查找操作。Matcher对象可以使用的3种匹配查找的方法如下:
• matches()方法:尝试对整个文本展开匹配检测,也就是只有整个文本串完全匹配表达式时才返回真值。
• lookingAt()方法:检测目标字符串是否以匹配的子串起始。
• find()方法:尝试在目标字符串里查找下一个匹配子串。
实现步骤
(1)新建一个类名为Java_Matcher.java。
(2)代码如下所示:
package chp07; import java.util.regex.Matcher; import java.util.regex.Pattern; public class Java_Matcher { static String str = "Is a good person."; public static void main(String[] args) { Pattern pa = Pattern.compile("\\bIs"); //生成Pattern对象并且编译一个简单的正则表达式"\bIs" Matcher ma = pa.matcher(str);//用Pattern类的matcher()方法生成一个Matcher对象 System.out.println("字符串:" + str); System.out.println("正则表达式:" + "\\bIs"); System.out.println("字符串的匹配结果:" + ma.matches()); System.out.println("子串匹配结果:" + ma.find()); System.out.println("匹配字符串的起始部分:" + ma.lookingAt()); } }
(3)运行结果如图7-19所示。
图7-19 运行结果
源程序解读
在本程序中通过Pattern.compile("\\bIs");生成Pattern对象并且编译一个简单的正则表达式“\bIs”,然后通过pa.matcher(str);语句调用Pattern类的matcher()方法生成一个Matcher对象。在这里需要说明的是正则表达式:\\bIs的含义是,Is必须是一个单词的开头。它转义后的表示形式为“\bIs”。因为“\\”本身就是一个转义字符。由于正则表达式都是以“\”开头,所以在Java中以字符串存储的时候,都以其转义字符形式存储,即多加一个“\”。
实例61 替换方法的使用
Math类不仅提供了可以进行匹配查找操作,同时也提供了替换指定字符串的操作。本实例将演示如何利用Math类实现将匹配子串替换成指定的字符串。
技术要点
在Math类中,提供了4种将匹配子串替换成指定的字符串的方法。其方法如下所示:
• replaceAll(String replacement):用replacement替换所有匹配成功的子串。
• replaceFirst(String replacement):用replacement替换第一个匹配成功的子串。
• appendReplacement(StringBuffer sb,String replacement):将当前匹配子串替换为指定字符串,并且将替换后的子串以及其之前到上次匹配子串之后的字符串段添加到一个StringBuffer对象里。
• appendTail(StringBuffer sb):将最后一次匹配工作后剩余的字符串添加到一个StringBuffer对象里。
实现步骤
(1)新建一个类名为Java_Replace.java。
(2)代码如下所示:
package chp07; import java.util.regex.Matcher; import java.util.regex.Pattern; public class Java_Replace { public static void main(String[] args) { String str = "Lili Zhang and Lili Zhao are both working in Beijing" + " Zhao's LiliShop "; Pattern p = Pattern.compile("Lili"); //生成Pattern对象并且编译一个简单的正则表达式"Lili" Matcher mc = p.matcher(str); //Pattern类的matcher()方法生成一个Matcher对象 StringBuffer sb = new StringBuffer(); int count = 0; boolean result; //使用循环将句子里所有的Lili找出并替换再将内容加到sb里 while (mc.find()) { mc.appendReplacement(sb, "Meray"); count++; System.out.println("第" + count + "次匹配后StringBuffer的内容是:" + sb+"\n"); } mc.appendTail(sb); //最后调用appendTail()方法将最后一次匹配后的剩余字符串加到StringBuffer里 System.out.println("调用appendTail方法后StringBuffer的最终内容是:" + sb.toString()); } }
(3)运行结果如图7-20所示。
图7-20 运行结果
源程序解读
在本程序中通过Pattern.compile("\\bIs");生成Pattern对象并且编译一个简单的正则表达式“\bIs”,然后通过pa.matcher(str);语句调用Pattern类的matcher()方法生成一个Matcher对象。最后通过调用Matcher对象的appendReplacement(),将程序中的所有Lili替换成了Meray。
实例62 检验E-mail的合法性
本实例将演示如何检验输入是否为合法的E-mail,这在表单中运用的非常多。该程序是利用正则表达式,检验一个输入的字符串是否为合法的E-mail。
技术要点
正则表达式的关键之处在于确定你要搜索匹配的东西,如何构造表达式,是应用正则表达式的重心,而构造一个表达式则需遵循6个基本规则:
(1)普通字符:字母、数字、汉字、下划线以及后面章节中没有特殊定义的标点符号,都是普通字符。表达式中的普通字符,在匹配一个字符串的时候,匹配与之相同的一个字符。
(2)简单的转义字符:一些不便书写的字符,采用在前面加“\”的方法,这些字符其实前面都已经介绍过了。
• \r:代表回车符。
• \n:代表换行符。
• \t:代表制表符。
• \\:代表“\”本身。
还有其他一些在后面章节中有特殊用处的标点符号,在前面加“\”后,就代表该符号本身。例如,^、$都有特殊意义,如果想要匹配字符串中“^”和“$”字符,则表达式就需要写成“\^”和“\$”。
• \^:匹配^符号本身。
• \$:匹配$符号本身。
• \.:匹配小数点“.”本身。
这些转义字符的匹配方法与普通字符是类似的,也是匹配与之相同的一个字符。
(3)能够与多种字符匹配的表达式:正则表达式中的一些表示方法,可以匹配多种字符之中的任意一个字符。例如,表达式“\d”可以匹配任意一个数字。虽然可以匹配任意字符,但是只能是一个,不是多个。这就好比玩扑克时,大小王可以代替任意一张牌,但是只能代替一张牌。
• \d:任意一个数字,0~9中的任意一个。
• \D:匹配所有的非数字字符。
• \w:任意一个字母、数字或下划线,也就是A~Z、a~z、0~9和_中任意一个。
• \W:用于匹配所有与\w不匹配不字符。
• \s:包括空格、制表符、换页符等空白字符之中的任意一个。
• \S:用于匹配除单个空格符之外的所有字符。
• \.:小数点可以匹配除了换行符(\n)以外的任意一个字符。
(4)自定义能够匹配多种字符的表达式:使用方括号[]包含一系列字符,能够匹配其中任意一个字符。用[^]包含一系列字符,则能够匹配其中字符之外的任意一个字符。同样的道理,虽然可以匹配其中任意一个,但是只能是一个,不是多个。
• [ab5@]:匹配a、b或5、@。
• [^abc]:匹配a、b、c之外的任意一个字符。
• [f-k]:匹配f~k之间的任意一个字符。
• [^A-F0-3]:匹配A~F、0~3之外的任意一个字符。
(5)修饰匹配次数的特殊符号的使用方法如下:
• {n}:表达式重复n次,如“\w{2}”相当于“\w\w”,“a{5}”相当于aaaaa。
• {m,n}:表达式至少重复m次,最多重复n次,如ba{1,3}可以匹配ba、baa或baaa。
• {m,}:表达式至少重复m次,如“\w\d{2,}”可以匹配a12、_456或M12344。
• ?:匹配表达式0次或者1次,相当于{0,1},如“a[cd]?”可以匹配a、ac或ad。
• +:表达式至少出现1次,相当于{1,},如a+b可以匹配ab、aab或aaab。
• *:匹配前一个字符零次或几次,如“cd*”可以匹配成c、cd、cddd。
(6)其他一些代表抽象意义的特殊符号:
• ^:与字符串开始的地方匹配,不匹配任何字符。
• $:与字符串结束的地方匹配,不匹配任何字符。
• \b:匹配一个单词边界,也就是单词和空格之间的位置,不匹配任何字符。
• \B:匹配非单词边界,即左右两边都是“\w”范围或者左右两边都不是“\w”范围时的字符缝隙。
实现步骤
(1)新建一个类名为ZengZe.java。
(2)代码如下所示:
package chp07; import java.util.Scanner; import java.util.regex.*; public class ZengZe { public static void main(String[] args) { String str =“hahxiao:12@sina.com”; //检测输入的E-mail地址是否以非法符号"."或"@"作为起始字符 Pattern p = Pattern.compile("^\\.|^\\@"); Matcher m = p.matcher(str); if (m.find()) { System.err.println("E-MAIL地址不能以'.'或'@'作为起始字符"); } //检测是否以"www."为起始 p = Pattern.compile("^www\\."); m = p.matcher(str); if (m.find()) { System.out.println("E-MAIL地址不能以'www.'起始"); } //检测是否包含非法字符 p = Pattern.compile("[^A-Za-z0-9\\.\\@_\\-~#]+"); m = p.matcher(str); StringBuffer sb = new StringBuffer(); boolean result = m.find(); boolean deletedIllegalChars = false; while (result) { //如果找到了非法字符那么就设下标记 deletedIllegalChars = true; //如果里面包含非法字符如冒号、双引号等,那么就把它们消去,加到SB里面 m.appendReplacement(sb, ""); result = m.find(); } m.appendTail(sb); String str1 = sb.toString(); if (deletedIllegalChars) { System.out.println("输入的E-MAIL地址里包含有冒号、逗号等非法字符,请修改"); System.out.println("您现在的输入为: " + str); System.out.println("修改后合法的地址应类似: " + str1); } } }
(3)运行结果如图7-21所示。
图7-21 运行结果
源程序解读
在本程序中通过Pattern.compile("[^A-Za-z0-9\\.\\@_\\-~#]+");生成Pattern对象并且编译校验是不是合法电子邮箱地址的正则表达式,然后通过Matcher m = p.matcher(str);语句调用Pattern类的matcher()方法生成一个Matcher对象m。最后通过调用m.appendReplacement()来实现电子邮箱的校验功能。