第11章 Java高级特性

Java提供了很多高级特性,包括基本数据类型的自动装箱(Auto Boxing)与拆箱(Unboxing)、新的for/in循环形式、可变长参数方法(Varargs Method)、静态导入(Import Static)、泛型(Generics)以及注释(Annotation)等,本章将会讲解这些新特性。

实例93 自动装箱与拆箱

自从Java颁布了自动装箱与拆箱的法规之后,int就能与Integer平起平坐。它们出现之前,必须把int变量封装成Integer对象,才能存储在List、Set或者Map中,因为它们只能存储对象,在从List、Set和Map中取int变量值时,需要先取出Integer对象,再调用Integer的intValue方法才能获得int值。本实例讲解如何进行自动装箱与拆箱。

技术要点

自动装箱与拆箱的技术要点如下:

• 自动装箱是把基本数据类型封装成类对象,拆箱是指把类对象拆成基本数据类型。

• 在表达式中,基本数据类型的数据值可以和基本数据对象进行运算。

• 基本数据类型的数组不能实现自动装箱和拆箱,即int[]不能当成Integer[]使用。

实现步骤

(1)新建一个类名为TextAutoboxAndUnbox.java。

(2)代码如下所示:

package com.zf.s11;                            //创建一个包
public class TextAutoboxAndUnbox {             //操作自动装箱与拆箱的类
    private static void testEqual() {          //测试对象型是否相等
          Integer i1 = 256;
          Integer i2 = 256;
          if (i1 == i2)
              System.out.println("相等!");
          else
              System.out.println("不相等!");
    }
    private static void autoBoxToInt() {       //自动装箱拆箱
          int i = 0;
          Integer integer = i;                 //基本类型值,可以赋值给Integer型变量
          int j = integer;                     //原始类型的数,也能赋值基本类型
          Integer counter = 1;                 //装箱
          int counter2 = counter;              //拆箱
          while (counter < 3) {
              System.out.println("计数" + counter++); //counter对象型的数,能自动增加
          }
      }
      public static void autoBoxToChar(){        //字符类型的自动拆箱与装箱
          char ch = 'A';
          Character character = 'B';             //装箱
          System.out.println("ch = " + ch + "; character = " + character);
          if (ch != character){                  //拆箱
              ch = character;
              System.out.println("ch = " + ch + "; character = " + character);
          }
      }
      public static void autoBoxToBoolean(){     //布尔类型的自动拆箱与装箱
          boolean b = false;
          Boolean bool = true;                   //装箱
          if (bool){                             //拆箱
              System.out.println("bool : " + true);
          }
          if (b || bool){                        //逻辑或
              b = bool;                          //拆箱
              System.out.println("bool : " + bool + "; b : " + b);
          }
      }
      private static void testWhile() {          //测试自动装箱与拆箱在循环中的影响
          Boolean bool = true;
          Integer number = 0;
          int captcity = 3;
          while (number < captcity) {
              if (bool) {
                    System.out.println("Welcome you "+number);
                    number++;                    //等价于number=number+1
              }
              else
                    number--;                    //等价于number=number-1
          }
      }
      public static void main(String []args){    //Java程序主入口处
          System.out.println("1.测试对象型是否相等");
          testEqual();                           //调用方法
          System.out.println("2.测试整数类型自动装箱与拆箱");
          autoBoxToInt();                        //调用方法
          System.out.println("3.测试字符类型自动装箱与拆箱");
          autoBoxToChar();                       //调用方法
          System.out.println("4.测试布尔类型自动装箱与拆箱");
          autoBoxToBoolean();                    //调用方法
          System.out.println("5.测试自动装箱与拆箱在循环中的影响");
          testWhile();                           //调用方法
      }
}

(3)运行结果如下所示:

1.测试对象型是否相等
不相等!
2.测试整数类型自动装箱与拆箱
计数 1
计数 2
3.测试字符类型自动装箱与拆箱
ch = A; character = B
ch = B; character = B
4.测试布尔类型自动装箱与拆箱
bool : true
bool : true; b : true
5.测试自动装箱与拆箱在循环中的影响
Welcome you 0
......

源程序解读

(1)testEqual()方法输出的结果是“不相等”。对于在内存中开辟的两个地址,JVM可以选择尝试对代码的最佳优化:在自动装箱时对于值从-128到127之间的值进行比较时返回true,否则返回false。

(2)autoBoxToInt()方法是对int与Integer之间进行装箱与拆箱,可以将整数值赋给Integer变量,也可以将Integer对象赋给int变量,Integer对象之间可以进行运算,Integer与int变量之间也可以进行运算。

(3)autoBoxToChar()方法是对char与Character之间进行装箱与拆箱。其他数据类型,如boolean、double、short、long等也能够进行自动装箱与拆箱。

实例94 for/in循环

本实例介绍新的for/in循环形式,可以方便地遍历数据、集合以及列表,甚至对象的所有属性。

技术要点

使用for/in循环的技术要点如下:

• for/in循环格式:for(a:b)。a变量是b中的元素类型,b可以是数组、集合或者列表。for/in循环将b中的元素赋给a,修改a的值不会影响到b。

• 数组、集合或者列表是依次按顺序赋值给变量,不能跳着遍历。

实现步骤

(1)创建一个类名为TextForIn.java。

(2)代码如下所示:

package com.zf.s11;                             //创建一个包
import java.util.ArrayList;                     //引入类
import java.util.List;
public class TextForIn {                    //操作运用for/in循环遍历数组、集合或列表的元素
    public static void getIntArraySum(int[] number)
              throws Exception{             //对整数数组求和
          if (number == null){
              throw new Exception("传入参数不能为空");
          }
          long sumResult = 0;
          for (int n : number){             //将取得数组值进行累加
              sumResult += n;
          }
          System.out.println("对整数数组进行求和,结果为:"+sumResult);
    }
    public static void getIntListSum(List<Integer> number)
              throws Exception{             //对整数列表求和
          if (number == null){
              throw new Exception("传入参数不能为空");
          }
          long result = 0;
          for (int n : number){             //可以用与遍历数组一样的方式遍历列表
              result += n;
          }
          System.out.println("对整数列表进行求和,结果:"+result);
    }
    public static void getIntAvgSum(int[][] number)
              throws Exception{             //求二维数组的平均值
          if (number == null){
              throw new Exception("传入参数不能为空");
          }
          long result = 0;
          long size = 0;
          for (int[] n: number){            //对于二维数组,每个数组元素都是一维数组
              for (int y : n){              //一维数组中的元素才是数字
                    result += y;            //求和
                    size ++;                //等价于size=size+1
              }
          }
          System.out.println("二维数组进行求和,结果:"+(result/size));
    }
    public static void main(String[] args) throws Exception {//Java程序主入口处
          int[] number = {12, 24, -1, 36, 42};    //声明数组并初始化
          List<Integer> list = new ArrayList<Integer>();      //声明一个集合
          for (int i=0; i<5; i++){                //循环对集合元素进行赋值
              list.add(number[i]);
          }
          int[][] numbers = {{1,2,3}, {4,5,6}, {7,8,9,10}};
          getIntArraySum(number);                 //调用整数数组求和方法
          getIntListSum(list);                    //调用整数列表求和方法
          getIntAvgSum(numbers);                  //调用二维数组的平均值方法
    }
}

(3)运行结果如下所示:

对整数数组进行求和,结果为:113
对整数列表进行求和,结果:113
二维数组进行求和,结果:5

源程序解读

(1)getIntArraySum()方法计算整数数组元素的和。通过for/in循环,数组按顺序依次将元素赋给变量,再进行累加求和。

(2)getIntListSum()方法计算列表中元素的和。其中List<Integer>类型是一个元素为Integer的列表。通过for/in循环按顺序依次将列表中的元素赋给变量,变量类型为Integer。由于int与Integer之间可以进行自动装箱与拆箱,可以将Integer类型转变为int类型。

(3)getIntAvgSum()方法计算二维数组中元素的平均值。对于二维数组,数组中的每个元素都是一维的,所以运用了双重for循环,先将二维数组元素赋给一维数组变量,再将一维数组元素赋给int类型变量。

实例95 参数不确定(可变长参数)

在没有可变长参数之前,传入到方法的参数个数不固定时,往往采用数组的方式传递参数。本实例介绍如何使用可变长参数的特性给方法传递参数。

技术要点

使用可变长参数的技术要点如下:

• 使用可变长参数,要在参数类型和参数名之间加“...”(三个英文点),表示参数是可变长的。

• 可以通过for/in循环读取可变长参数中的元素值。

实现步骤

(1)创建一个类名为TextVarargs.java。

(2)代码如下所示:

package com.zf.s11;                         //创建一个包
public class TextVarargs {                  //操作可变长参数的类
    public static void printInfo(boolean isflog,
              String... info) {             //打印消息,消息数量可以任意多
          if (isflog) {                     //打印参数的长度
              System.out.println("待打印消息的长度" + info.length);
          }
          for (String s : info) {           //使用for/in循环遍历参数
              System.out.println(s);
          }
          if (isflog) {
              System.out.println("打印消息结束");
          }
    }
    public static void printInfo(int number,
              String... info) {             //重载printInfo方法
        if (number != 0) {                  //打印消息的长度
              System.out.println("待打印消息的个数为" + info.length);
        }
        for (String s : info) {             //使用for/in循环遍历参数
              System.out.println(s);
        }
        if (number != 0) {
              System.out.println("打印消息结束");
        }
    }
    public static double avg(double... values) {     //取平均数
        double total = 0;
        int count = values.length;
        for (double i : values) {                    //循环遍历参数
              total += i;
        }
        return total / count;                        //返回平均数
    }
    public static void main(String[] args) {         //Java程序主入口处
        printInfo(true, "第一个", "第二个", "第三个"); //调用方法
        printInfo(3, "第一块", "第二块");              //调用方法
        System.out.printf("平均数为%s.%n", avg(3.0, 4.2, 3.6));//调用取平均数方法
    }
}

(3)运行结果如下所示:

待打印消息的长度3
第一个
第二个
第三个
打印消息结束
待打印消息的个数为2
第一块
第二块
打印消息结束
平均数为3.6.

源程序解读

(1)第一个printInfo()方法根据传入的布尔类型值,打印可变长参数的长度,通过for/in循环,将传入的可变长参数的值进行输出。

(2)第二个printInfo()方法是运用类的特性—多态,根据传入的参数不同进行重载。通过循环将传入的可变长参数值输出。

(3)avg()方法是对传入的浮点型可变长参数进行求和运算。运用for/in循环对参数值进行累加。

(4)在程序main()方法中运用System.out.printf()方法,其中%n表示输出一个回车符,%s表示输出一个字符串,内容就是后面的可变长参数的第一项。

实例96 方法改变(协变式返回类型)

协变式返回类型允许在覆盖父类方法的时候,使父类方法的返回值更加具体。本实例介绍如何使用协变式返回类型。

技术要点

使用协变式返回类型的技术要点如下:

• 协变式返回类型的基本用法是用于在已知一个实现的返回类型比API更具体的时候进行类型强制转换(一般是子类比其父类更具体)。

• 在已知继承关系的情况下,使用其子类的返回类型来“重载”其接口实现。

实现步骤

(1)创建一个类名为TextCovariant.java。

(2)代码如下所示:

package com.zf.s11;                            //创建一个包
import java.util.Vector;
public class TextCovariant extends Vector {    //操作协变式返回类型的类,继承Vector类
    private static final long serialVersionUID = 1L;  //默认串行版本标识
    @Override
    public String get(int index) {                    //重写Vector中的get()方法
          return (String) super.get(index);
    }
    @Override
    public String firstElement() {             //重写Vector中的firstElement()方法
          return (String) super.firstElement();        //获得第一个元素
    }
    public static void main(String[] args) {           //Java程序主入口处
          TextCovariant text = new TextCovariant();    //创建实例对象
          text.add("first");                           //添加对象
          text.add("second");
          String get = text.get(1);        //可以直接返回String类型,不是父类的Object
          String first = text.firstElement();          //可以直接返回String类型
          System.out.printf("重载get()方法返回String,结果:%s%n", get);
          System.out.printf("重载firstElement()方法返回String,结果:%s%n", first);
    }
}

(3)运行结果如下所示:

重载get()方法返回String,结果:second
重载firstElement()方法返回String,结果:first

源程序解读

(1)get()方法中,覆盖父类Vector的get()方法。父类get()方法返回的是Object,在这方法里返回String类型。get()方法中传入的整数参数是指定的集合中的下标(索引)。

(2)firstElement()方法中,覆盖父类的firstElement()方法。同样,父类firstElement()方法返回的是Object方法,该方法返回String类型。

实例97 静态导入

Java可以进行静态导入,导入某个类的静态方法或静态变量。在使用时不需要再指明类名,直接应用变量或者方法名即可。本实例介绍如何使用静态导入。

技术要点

使用静态导入的技术要点如下:

• Import static为新增的一个指令。写import语句时,可以定位到一个静态方法或静态变量。

• 不允许静态方法和静态变量出现重名。

• 可以使用通配符“*”代表导入该类的所有静态变量或静态方法。

实现步骤

(1)创建一个类名为TextStaticImport.java。

(2)代码如下所示:

package com.zf.s11;                             //创建一个包
import static java.lang.Math.PI;                //导入静态变量Math.PI
import static java.lang.Math.max;               //导入静态方法Math.max()
import static java.lang.Math.min;               //导入静态方法Math.min()
import static java.lang.Math.sin;               //导入静态方法Math.sin()
import static java.lang.Integer.*;              //导入Integer所有静态方法和属性
public class TextStaticImport { //操作静态导入的类 public static void main(String[] args) { //Java程序主入口处 //通过静态导入使用Math的静态方法 System.out.printf("判断最大值:%s%n",max(3, 6)); System.out.printf("判断最小值:%s%n",min(1,5)); System.out.printf("π的正弦值sin(%s)=%s%n",PI,sin(PI)); //通过静态导入使用Integer的静态方法 System.out.printf("将字符串转化为整数%s%n",parseInt("123")); System.out.printf("十进制转为二进制%s%n",toBinaryString(12)); System.out.println(MAX_VALUE); //通过静态导入使用Integer的静态属性 System.out.println(MIN_VALUE); } }

(3)运行结果如下所示:

判断最大值:6
判断最小值:1
π的正弦值sin(3.141592653589793)=1.2246467991473532E-16
将字符串转化为整数123
十进制转为二进制1100
2147483647
-2147483648

源程序解读

将java.lang.Math类的PI静态变量,max()、min()和sin()静态方法静态导入到类中,类可以直接使用这些静态变量和静态方法,无须使用Math前缀。同样将Integer的所有静态变量导入到类中,直接使用Integer的MAX_VALUE和MIN_VALUE静态变量,而无须使用Integer前缀。

实例98 动物搭配(泛型)

泛型的本质是参数化类型,也就是说所操作的数据类型被定为一个参数,这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法。本实例介绍如何使用泛型通配符、操作泛型类与方法。

技术要点

使用泛型的技术要点如下:

• 类泛型由类名后面跟上一个或几个由三角括号包含的类型变量组成。方法泛型主要体现在返回类型与方法内部。

• 泛型通配符问号(?)表示任意类型,如ArrayList<?>表示可以存入任意对象类型的ArrayList。通配符与extends连用表示指定类型及其所有子类,如List<? extends 父类>表示可以存放父类及其子类的对象的List。通配符与super连用表示指定类型及指定类型的所有父类,如List<? super子类>表示可以存放子类及其父类的对象的List。

• 通用类型是指不指定参数或者返回值的类型,常用一个大写的字母代表类型,它能代表任何类型,需要在方法声明的返回值前用<>声明通用类型。如public <K> String getData(K data)的方法声明中,String前用<K>表示K是通用类型,它的data参数的类型是K,表示能接收任意类型的参数,方法的返回值为String类型。

实现步骤

(1)新建一个类名为TextGeneric.java。

(2)代码如下所示:

package com.zf.s11;                              //创建一个包
import java.util.ArrayList;                      //引入类
import java.util.Collection;
import java.util.List;
class Animal {                                   //动物父类
    public String name;                          //名字
    public Animal(String name) {
          this.name = name;
    }
    public String toString() {
          return "动物的名字: " + this.name;
    }
}
class Cat extends Animal {                       //动物子类
    public String sound;                         //声音
    public Cat(String name, String sound) {
          super(name);                           //使用父类的名字
          this.sound=sound;
    }
    public String toString() {
          return super.toString() + ",它能"+sound;
    }
}
public class TextGeneric {                       //操作泛型的类
    public static void operatorCollection(Collection<?> collect){//使用通配符,?代表任何类型
          if (collect == null) {                 //判断对象是否为空
              return;
          }
          for (Object obj : collect) {           //循环遍历集合元素
              System.out.print(obj + "    ");
          }
          System.out.println();                  //换行
    }
    //使用有限制的通配符? extends,可以接受任何父类及其子类
    public static void displayName(Collection<? extends Animal> collect) {
          if (collect == null) {                  //判断对象是否为空
              return;
          }
          for (Animal animal : collect) {         //循环遍历动物集合元素
              System.out.print(animal.name + "    ");
          }
          System.out.println();
    }
    public static <K> List<K> arrayToList(K[] data) {//泛型方法,将一个任意类型数组添加到列表
          if (data == null) {                     //判断对象是否为空
              return null;
          }
          List<K> list = new ArrayList<K>();      //创建一个列表,K代表任意一个类
          for (K x : data) {                      //循环将元素放入列表中
              list.add(x);
          }
          return list;
    }
    //泛型方法,在一组Animal对象中查找名字为name的Animal对象
    public static <K extends Animal> K findAnimal(K[] animal, String name) {
          if (animal == null) {                   //判断
              return null;
          }
          K parent = null;                        //K可以是父类对象或者子类对象
          for (K x : animal) {                    //依次遍历Animal对象组
              //如果x对象的name与参数name匹配,则退出遍历
              if (x.name.equals(name)) {
                    parent = x;
                    break;
              }
          }
          return parent;                           //返回
    }
    public static void main(String[] args) {    //Java程序主入口处
        System.out.println("1.目标类型已指定");
        List<String> list_string = new ArrayList<String>();//指定具体的类型只装String类型
        list_string.add("one");                 //添加元素
        list_string.add("two");                 //只能装String类型,否则会出错
        list_string.add("three");
        String first = list_string.get(0);      //列表中取值,不用作强制类型转换
        String second = list_string.get(1);
        String three = list_string.get(2);
                                                //需要三个参数
        System.out.printf("第一个元素%s,第二个元素%s,第三个元素%s%n",first,second,three);
        System.out.println("2.泛型通配符以及继承");
        List<Integer> list_Int = new ArrayList<Integer>(); //创建列表,只能放Integer类型
        list_Int.add(10);
        list_Int.add(20);
        list_Int.add(30);
                                                //调用具有"?"通配符的方法
        //该方法既可以打印整型列表,也可以打印字符串列表
        operatorCollection(list_Int);
        operatorCollection(list_string);
        List<Animal> animals = new ArrayList<Animal>(); //列表只能放Animail及其子类对象
        animals.add(new Animal("动物1"));
        animals.add(new Animal("动物2"));
        animals.add(new Animal("动物3"));
        List<Cat> cats = new ArrayList<Cat>(); //列表只能放Cat及其父类对象
        cats.add(new Cat("小羊", "咩咩叫"));
        cats.add(new Cat("小狗", "汪汪叫"));
        cats.add(new Cat("小猫", "咪咪叫"));
        displayName(animals);                   //调用具有"?extends"通配符的方法
        displayName(cats);                      //只接受父类以及子类类型的列表
        System.out.println("3.泛型方法");
        Integer[] obj = { 11, 22, 33, 44, 55 };
        //调用方法将任意类型的对象数组转换成整型数组
        List<Integer> resultInt = arrayToList(obj);
        operatorCollection(resultInt);         //调用方法打印整型列表
        String[] ss = { "cat", "dog", "cattle", "sheep", "horse" };
        List<String> result = arrayToList(ss); //调用方法转换字符串数组
        operatorCollection(result);            //调用方法打印字符串列表
        Animal[] animal = { new Animal("动物1"), new Animal("动物2"),
                  new Animal("动物3") };        //创建对象数组并初始化
        Animal parent = findAnimal(animal, "动物1"); /根据名字进行查找
        System.out.println("找到的动物1:" + parent);
        Cat[] cat = { new Cat("小羊", "咩咩叫"), new Cat("小狗", "汪汪叫"),
                  new Cat("小猫", "咪咪叫") };
        Cat child = findAnimal(cat, "小羊");
        System.out.println("找到的小羊:" + child);
    }
}

(3)运行结果如下所示:

1.目标类型已指定
第一个元素one,第二个元素two,第三个元素three
2.泛型通配符以及继承
10       20      30
one      two     three
动物1    动物2    动物3
小羊      小狗      小猫
3.泛型方法
11    22    33    44    55
cat    dog    cattle    sheep    horse
找到的动物1:动物的名字: 动物1
找到的小羊:动物的名字: 小羊,它能咩咩叫

源程序解读

(1)operatorCollection()方法使用了带通配符的参数的方法,输出Collection中的元素。参数为Collection<?> collect,表示能接收任意类型的Collection,如Collection<Integer>、Collection<String>等。通过新的for循环,读取Collection的元素,因为Java中一切对象都继承Object,所以,可以将Collection的元素赋给Object变量。

(2)displayName()方法使用了带限制通配符的参数的方法,输出Animal的name属性。参数为Collection<? extends Animal> collect,表示它能接收类型为Animal及其子类的Collection,如Collection<Animal>、Collection<Cat>等。通过新的for循环读取Collection的元素,因为Collection中的所有元素都是Animal或者它的子类类型,所以可以将Collection的元素赋给Animal变量。

(3)arrayToList()方法使用了通用类型的参数和返回值,将一个任意类型的数组转换成相应的List。static后面的<K>表示K是一个通用类型,参数K[] data表示data是任意类型的数组,如String[]、Integer[]等,返回值为List <K>,表示返回一个K类型的List。使用新的for循环读取data数组的元素,因为data中的元素类型都为K,所以可以将data的元素赋给K变量,然后将K变量添加到List<K>中。

(4)findAnimal()方法使用了带限制通用类型的参数和返回值,从一组Animal对象中,查找名字为name参数的Animal对象。static后面的<K extends Animal>表示K是一个继承Animal的通用类型。参数K[]animal表示animal是K类型的数组,如Animal []、Cat[]等,返回值为K,表示返回一个Animal或者它的子类的对象。因为K继承Animal,所有K具备Animal的name属性。

实例99 人员信息(枚举类型)

本实例介绍枚举类型。枚举类型是用enum声明,看起来像数组的一组值。

技术要点

使用枚举类型的技术要点如下:

• 枚举类型中的变量是实实在在的对象,并且它们是public(公共的)、static(静态的)、final(引用不可修改)的。

• enum内部可以有构造方法,但不能有public的构造方法。enum内置间必须使用逗号隔开,声明完所有的内置对象之后用分号结束。Enum内置对象必须使用声明的构造函数,否则编译错误。

• 可以在枚举类型中定义非枚举值变量,这些变量可以使用任何修饰符。

• 变量和方法的定义必须在枚举值后面定义。

实现步骤

(1)新建一个类名为TextEnumeration.java。

(2)代码如下所示:

package com.zf.s11;                             //创建一个类
public class TextEnumeration {                  //操作枚举的类
    static enum RESULT{YES,NO,UNKOWN,};
    static enum Type{
          MANAGER("经理",5000),
          ASSISTANT("助理",3000),
          WORKER("员工",1000);
          private String name;                   //只能在枚举值的后面
          private double pay;
          Type(String name,double pay){          //带参数的构造方法
              this.name=name;
              this.pay=pay;
          }
    }
    enum Person{
          CHINESE("中国人"){
              public String toString(){          //返回对应值
                    return "这是一个中国人";
              }},
          AMERICAN("美国人"),
          ENGLISHMAN("英国人"){                   //返回对应值
              public String toString(){
                    return "这是一个英国人";
          }},
          OTHERS{
              public String toString(){          //返回对应值
                    return "这是一个其他国家的人";
          }};
          private String value = null;           //枚举值的属性只能声明在枚举值后面
          Person(){                              //默认构造方法
              value = "其他人";
          }
          Person(String value){                  //带参数的构造方法
              this.value = value;
          }
          public String getValue(){              //获得value值
              return this.value;
          }
    }
    static class Human{                          //创建静态内部类
          public Human(String humanName,Type type){//带参数构造方法
              this.humanName=humanName;
              this.type=type;
          }
          private String humanName;               //人员名称
          private Type type;                      //人员类型
          public String getHumanName() {
              return humanName;
          }
          public void setHumanName(String humanName) {
              this.humanName = humanName;
          }
          public Type getType() {
              return type;
          }
          public void setType(Type type) {
              this.type = type;
          }
    }
    public static void displayResult(){          //操作RESULT枚举
          System.out.printf("RESULT.YES.toString():\t%s %n",
                  RESULT.YES.toString());        //返回枚举变量的名称
          System.out.printf("RESULT.YES.ordinal():\t%s %n",
                  RESULT.YES.ordinal());         //返回枚举位置
          System.out.printf("RESULT.YES.getClass():\t%s %n",
                  RESULT.YES.getClass());        //返回枚举变量的类信息
          System.out.println("遍历Set枚举的元素");
          for(RESULT result: RESULT.values()){   //循环遍历枚举变量
              System.out.printf("%s,\t",result);
          }
          switch(RESULT.YES){                    //进行枚举判断
          case YES:
              System.out.println("显示结果:"+RESULT.YES);
              break;
          case NO:
              System.out.printf("显示结果:"+RESULT.YES);
              break;
          default:
              System.out.println("未知!");
          }
    }
    public static void displayType(){            //操作Type枚举
          Human human1=new Human("Susan",Type.MANAGER);//实例化对象并初始化
          Human human2=new Human("Lingda",Type.ASSISTANT);
          Human human3=new Human("Lisa",Type.WORKER);
          System.out.printf("姓名:%1$s,\t职业%2$s,\t薪水:%3$s %n",
human1.getHumanName(),human1.getType().name,human1.getType().pay);
          System.out.printf("姓名:%1$s,\t职业%2$s,\t薪水:%3$s %n",
human2.getHumanName(),human2.getType().name,human2.getType().pay);
          System.out.printf("姓名:%1$s,\t职业%2$s,\t薪水:%3$s %n",
human3.getHumanName(),human3.getType().name,human3.getType().pay);
    }
    public static void displayPerson(){          //操作Person枚举
          Person person=Person.CHINESE;
          System.out.printf("person.toString():%s%n",
                    person);                      //CHINESE枚举值覆盖toString()方法
          person=Person.ENGLISHMAN;//
          System.out.printf("person.toString():%s%n",
                    person);                      //ENGLISHMAN枚举值覆盖toString()方法
          person=Person.OTHERS;
          System.out.printf("调用getValue():%s%n",
                    person.getValue());           //调用枚举方法
      }
      public static void main(String []args){     //Java程序主入口处
          System.out.println("1.显示RESULT枚举信息");
          displayResult();                        //调用方法显示RESULT枚举信息
          System.out.println("2.显示Type枚举信息");
          displayType();                          //调用方法显示Type枚举信息
          System.out.println("3.显示Person枚举信息");
          displayPerson();                        //调用方法显示Person枚举信息
      }
}

(3)运行结果如下所示:

1.显示RESULT枚举信息
RESULT.YES.toString(): YES
RESULT.YES.ordinal():  0
RESULT.YES.getClass(): class com.zf.s11.TextEnumeration$RESULT
遍历Set枚举的元素
YES,            NO,          UNKOWN,        显示结果:YES
2.显示Type枚举信息
姓名:Susan,     职业经理,     薪水:5000.0
姓名:Lingda,    职业助理,     薪水:3000.0
姓名:Lisa,       职业员工,     薪水:1000.0
3.显示Person枚举信息
person.toString():这是一个中国人
person.toString():这是一个英国人
调用getValue():其他人

源程序解读

(1)RESULT是一个简单描述结果的枚举类型,定义了3个枚举值:YES、NO和UNKOWN。每个枚举值都是一个RESULT实例。

(2)Type是一个包含扩展属性、构造方法和内置的枚举类型对象。各个枚举对象之间用逗号隔开。构造方法不能用访问修饰符,它们都是private的。

(3)Person是一个更为复杂的枚举类型。在定义的枚举值的后面声明了value私有属性,并声明多个构造方法和一个公共获得value值的方法。其中枚举值CHINESE使用了第二个构造方法,传入的value为“中国人”,枚举值OTHER使用第一个构造方法,将value赋值为“其他人”。还可以为枚举值声明方法,通过枚举值可以访问这些方法。

(4)displayResult()方法中,枚举值的toString()方法获得枚举变量的名称;枚举值的ordinal()方法获得它在枚举类型中的位置;枚举值的getClass()获得变量的类信息。通过循环依次显示枚举变量。通过switch...case分支语句判断枚举的值。

(5)displayType()方法,根据构建的内部静态类,创建三个类实例,并结合枚举值进行初始化,再将数据打印到控制台。其中“%1$s”表示第一个字符串,“%n$s”表示按顺序填写的第n个字符串。

(6)displayPerson()方法中,由于每个枚举值都是一个对象。所以根据Person枚举值创建了不同的实例。枚举值覆盖toString()方法,方法获得Person中对应的枚举的字符串形式。

实例100 printf()用法

System.out.printf()方法既吸收了C/C++里printf()方法的优点,又有自身的特殊用法,使之更加灵活和全面。本实例介绍printf()方法的主要用法。

技术要点

Java中printf()用法的技术要点如下:

• printf功能是产生格式化输出的函数,printf()方法的第一个参数为输出的格式,第二个参数是可变长的,表示待输出的数据对象。格式为:printf(格式控制,输出表列),如printf("i=%d, ch=%c\n", i, ch)。“格式控制”是用双撇号括起来的字符串,也称“转换控制字符串”,它包括两种信息:一是格式说明,由“%”和格式字符组成,它的作用是将输出的数据转换为指定的格式输出;二是普通字符,即需要原样输出的字符。“输出表列”是需要输出的一些数据,可以是表达式。printf函数的一般形式可以表示为printf (参数1,参数2,⋯⋯,参数n),功能是将参数2~参数n按参数1给定的格式输出。

• 除了X、E、G(用大写字母表示)外,其他格式字符必须用小写字母。“格式控制”字符串内可以包含转义字符。如果想输出字符“%”,则应该在“格式控制”字符串中用连续两个%表示,如printf("%f%%", 1.0/3);

实现步骤

(1)新建一个类名为TextPrintf.java。

(2)代码如下所示:

package com.zf.s11;                                  //创建一个包
import java.util.Date;
public class TextPrintf {                            //操作printf()方法的类
    public static void printString(){                //输出不同格式字符串
          System.out.printf("%s", "这是一个字符串");//%s表示输出字符串,将后面字符串替换%s
          System.out.printf("%s%n", "end line");     //%n表示换行
          System.out.printf("%s = %s%n", "folk", "HanZu");//还可以支持多个参数
          System.out.printf("%S = %s%n", "folk", "Han Nationality");//%S将字符串大写形式输出
          //多个参数时,可以在%s之间插入变量编号,1$表示第一个字符串,3$表示第3个字符串
          System.out.printf("%1$s = %3$s %2$s%n",
                    "folk", "Nationality", "Han");
    }
    public static void printBoolean(){               //输出不同格式的布尔类型
          System.out.printf("true = %b; false = ", true);     //%b表示输出布尔类型
          System.out.printf("%b%n", false);          //%表示换行
    }
    public static void printInt(){                   //输出不同格式的整数
        Integer obj = 36;
        System.out.printf("%d; %d; %d%n",
                  -10, 23L,obj);                     //%d表示将整数格式化为十进制整数
        System.out.printf("%o; %o; %o%n",
                  -10, 23L, obj);                    //%o表示将整数格式化为八进制整数
        System.out.printf("%x; %x; %x%n",
                  -10, 23L, obj);                    //%x表示将整数格式化为十六进制整数
        //%X表示将整数格式化为十六进制整数,并且字母变成大写形式
        System.out.printf("%X; %X; %X%n", -10, 23L, obj);
    }
    public static void printFloatingPoint(){         //输出不同格式的浮点类型
        Double obj = 3.2d;
        System.out.printf("%e; %e; %e%n",            //%e表示以科学计数法输出浮点数
                  -72.000f, 123.23456d, obj);
        //%E表示以科学计数法输出浮点数,并且为大写形式
        System.out.printf("%E; %E; %E%n",
                  -72.000f, 123.23456d, obj);
        System.out.printf("%f; %f; %f%n",            //%f表示以十进制格式化输出浮点数
                  -72.000f, 123.23456d, obj);
        System.out.printf("%.1f; %.3f; %f%n",        //还可以限制小数点后的位数
                  -72.000f, 123.23456d, obj);
    }
    public static void printTime(){                  //输出不同格式的日期
        Date date = new Date();
        long time = date.getTime();
                                                     //格式化年月日
                                                     //%t表示格式化日期时间类型
                                                     //%T是时间日期的大写形式
        //%t之后用y表示输出日期的年份(2位数的年,如99)
        //%t之后用m表示输出日期的月份,%t之后用d表示输出日期的日号
        System.out.printf("%1$ty-%1$tm-%1$td; %2$ty-%2$tm-%2$td%n", date, time);
        //%t之后用Y表示输出日期的年份(4位数的年)
        //%t之后用B表示输出日期的月份的完整名, %t之后用b表示输出日期的月份的简称
        System.out.printf("%1$tY-%1$tB-%1$td; %2$tY-%2$tb-%2$td%n", date, time);
        //%t之后用D表示以 "%tm/%td/%ty"格式化日期
        System.out.printf("%1$tD%n", date);
        //%t之后用F表示以"%tY-%tm-%td"格式化日期
        System.out.printf("%1$tF%n", date);
                                                     //输出时分秒
        //%t之后用H表示输出时间的时(二十四进制),%t之后用I表示输出时间的时(十二进制),
        //%t之后用M表示输出时间的分,%t之后用S表示输出时间的秒
        System.out.printf("%1$tH:%1$tM:%1$tS; %2$tI:%2$tM:%2$tS%n", date, time);
                                                        //%t之后用L表示输出时间的秒中的毫秒
        System.out.printf("%1$tH:%1$tM:%1$tS %1$tL%n", date);
                                                        //%t之后p表示输出时间的上午或下午信息
        System.out.printf("%1$tH:%1$tM:%1$tS %1$tL %1$tp%n", date);
        System.out.printf("%1$tR%n", date);    //%t之后用R表示以"%tH:%tM"格式化时间
        System.out.printf("%1$tT%n", date);    //%t之后用T表示"%tH:%tM:%tS"格式化时间
        System.out.printf("%1$tr%n", date); //%t之后用r表示"%tI:%tM:%tS %Tp"格式化时间
        System.out.printf("%1$tF %1$tA%n", date); //%t之后用A表示得到星期几的全称
        System.out.printf("%1$tF %1$ta%n", date); //%t之后用a表示得到星期几的简称
        System.out.printf("%1$tc%n", date);            //输出时间日期的完整信息
    }
    public static void main(String[] args) {           //Java程序主入口处
        System.out.println("1.输出不同格式字符串");
        printString();                                 //调用方法
        System.out.println("2.输出不同格式的布尔值");
        printBoolean();                                //调用方法
        System.out.println("3.输出不同格式的整数");
        printInt();                                    //调用方法
        System.out.println("4.输出不同格式的浮点型");
        printFloatingPoint();                          //调用方法
        System.out.println("5.输出不同格式的日期类型");
        printTime();                                   //调用方法
    }
}

(3)运行结果如下所示:

1.输出不同格式字符串
这是一个字符串end line
folk = HanZu
FOLK = Han Nationality
folk = Han Nationality
2.输出不同格式的布尔值
true = true; false = false
3.输出不同格式的整数
-10; 23; 36
37777777766; 27; 44
fffffff6; 17; 24
FFFFFFF6; 17; 24
4.输出不同格式的浮点型
-7.200000e+01; 1.232346e+02; 3.200000e+00
-7.200000E+01; 1.232346E+02; 3.200000E+00
-72.000000; 123.234560; 3.200000
-72.0; 123.235; 3.200000
5.输出不同格式的日期类型
09-04-02; 09-04-02
2009-四月-02; 2009-四月-02
04/02/09
2009-04-02
15:08:00; 03:08:00
15:08:00 609
15:08:00 609 下午
15:08
15:08:00
03:08:00 下午
2009-04-02 星期四
2009-04-02 星期四
星期四 四月 02 15:08:00 CST 2009

源程序解读

(1)printString()方法中,格式为“%s”,表示以字符串的形式输出第二个可变长参数的第一个参数值;格式为“%n”,表示换行;格式为“%S”,表示将字符串以大写形式输出;在“%s”之间用“n$”表示输出可变长参数的第n个参数值。

(2)printBoolean()方法中,格式为“%b”,表示以布尔值的形式输出第二个可变长参数的第一个参数值。

(3)printInt()方法中,格式为“%d”,表示以十进制整数形式输出整数;格式为“%o”,表示以八进制整数形式输出;格式为“%x”,表示以十六进制输出;格式为“%X”,表示以十六进制输出,并且将字母(A、B、C、D、E、F)换成大写。

(4)printFloatingPoint()方法中,格式为“%e”,表示以科学计数法输出浮点数;格式为“%E”,表示以科学计数法输出浮点数,而且将e大写;格式为“%f”,表示以十进制浮点数输出,在“%f”之间加上“.n”表示输出时保留小数点后面n位。

(5)printTime()方法中,格式为“%t”,表示输出时间日期类型。“%t”之后用y表示输出日期的二位数的年份(如99),用m表示输出日期的月份,用d表示输出日期的日号;“%t”之后用Y表示输出日期的四位数的年份(如1999),用B表示输出日期的月份的完整名,用b表示输出日期的月份的简称。“%t”之后用D表示以“%tm/%td/%ty”的格式输出日期,用F表示以“%tY-%tm-%td”的格式输出日期。“%t”之后用H表示输出时间的时(二十四进制),用I表示输出时间的时(十二进制),用M表示输出时间的分,用S表示输出时间的秒,用L表示输出时间的秒中的毫秒数,用p表示输出时间是上午还是下午。“%t”之后用R表示以“%tH:%tM”的格式输出时间,用T表示以“%tH:%tM:%tS”的格式输出时间,用r表示以“%tI:%tM:%tS %Tp”的格式输出时间。“%t”之后用A表示输出日期的星期全称,用a表示输出日期的星期简称。

实例101 使用ProcessBuilder调用外部命令

ProcessBuilder类用于创建操作系统进程。能够设置目录、环境变量以及读取物理网卡的地址。本实例介绍如何使用该类调用外部命令并返回大量结果以及如何设置目录和环境参数。

技术要点

使用ProcessBuilder调用外部命令的技术要点如下:

• 每个ProcessBuilder实例管理一个进程属性集。start()方法利用这些属性创建一个新的Process实例。Start()方法可以从同一实例重复调用,以利用相同的或相关的属性创建新的子进程。

• 每个进程生成管理命令、环境、工作目录、redirectErrorStream等属性。

• 命令属性是一个字符串列表,它表示要调用的外部程序文件及其参数(如果有)。在此,表示有效的操作系统命令的字符串列表是依赖于系统的。例如,每一个总体变量,通常都要成为此列表中的元素,但有一些操作系统,希望程序能自己标记命令行字符串。在这种系统中,Java实现可能需要命令确切地包含这两个元素。

• 环境属性是从变量到值的依赖于系统的映射。初始值是当前进程环境的一个副本。

• 工作目录属性默认值是当前进程的当前工作目录,通常根据系统属性user.dir来命名。

• redirectErrorStream属性最初属性值为false,意思是子进程的标准输出和错误输出被发送给两个独立的流,这些流可以通过Process.getInputStream()和Process.getErrorStream()方法来访问。如果将值设置为true,标准错误将与标准输出合并。这使得关联错误消息和相应的输出变得更容易。在此情况下,合并的数据可从Process.getInputStream()返回的流读取,而从Process.getErrorStream()返回的流读取将直接到达文件尾。

• 修改进程构建器的属性将影响后续由该对象的start()方法启动的进程,但从不会影响以前启动的进程或Java自身的进程。

实现步骤

(1)新建一个类名为TextCMDExecute.java。

(2)代码如下所示:

package com.zf.s11;                        //创建一个包
import java.io.*;                          //引入类
import java.util.Iterator;
import java.util.Map;
public class TextCMDExecute {              //操作ProcessBuilder调用外部命令的类
    public synchronized void lookDirectory() throws Exception {//查看目录
                                                       //创建进程管理实例,查看目录
          ProcessBuilder builder = new ProcessBuilder("cmd", "/c", "dir");
          builder.directory(new File("F:/"));          //查看的目录
          Process process = builder.start();           //启动进程
          InputStream is = process.getInputStream();   //获得输入流
                                                       //创建输入读流,编码方式为GBK
          InputStreamReader isr = new InputStreamReader(is, "GBK");
          BufferedReader br = new BufferedReader(isr); //创建读缓冲对象
          String line;
          while ((line = br.readLine()) != null) {     //循环读取数据
              System.out.println(line);
          }
    }
                                                       //设置环境变量
    public synchronized void setEnvironment() throws Exception {
          ProcessBuilder pb = new ProcessBuilder("cmd.exe");//创建进程管理实例
          Map<String, String> env = pb.environment();  //获取系统参数并打印显示
          env.put("key1", "value2");                   //设置环境变量
          env.put("key2", "value2");
          env.remove("key2");                          //移除环境变量
          Iterator it = env.keySet().iterator();       //根据键值获得集合
          while (it.hasNext())                         //遍历集合显示系统变量
          {
              System.out.println("系统变量:" + ((String) it.next()) + "="
                        + env.get((String) it.next()));
          }
          pb.directory(new File("F:/temp"));           //设置工作目录
          pb.start();                                  //启动进程
    }
    public synchronized void lookPhysicalAddress()
              throws Exception {                       //查看物理地址
          try {
              ProcessBuilder pb = new ProcessBuilder("ipconfig", "/all");//创建进程管理实例
              Process process = pb.start();                  //启动进程
              byte[] b = new byte[1024];                     //创建字节数组
              StringBuffer sb = new StringBuffer();          //创建缓冲字符串
              InputStream is = process.getInputStream();     //获得输入流
              while (is.read(b) != -1) {                     //循环读取输入流中的数据
                    sb.append(new String(b));
              }
              is.close();
              String str = sb.toString();                    //转化为字符串
              String physical = "Physical Address. . . . . . . . . :";
              int i = str.indexOf(physical);                 //获得字符串所在的下标
              while (i > 0) {
                    str = str.substring(i + physical.length()); //截取字符串
                    System.out.printf("物理地址:%s%n", str.substring(1, 18));
                    i = str.indexOf(physical);          //获得physical字符串所在的下标
              }
          } catch (Exception e) {                       //捕获异常
              e.printStackTrace();
          }
      }
      public synchronized void openWNWB() throws Exception {//启动万能五笔
                                                        //创建进程管理实例
          ProcessBuilder p = new ProcessBuilder("D:\\TDDOWNLOAD\\万能五笔\\wnwb.exe");
          p.start();                                    //启动进程
      }
      public static void main(String[] args) throws Exception {//Java程序主入口处
          TextCMDExecute execute = new TextCMDExecute();//创建实例
          System.out.println("1.查看目录");
          execute.lookDirectory();                      //调用方法查看目录
          System.out.println("2.设置查看环境变量");
          execute.setEnvironment();                     //调用方法设置变量
          System.out.println("3.查看物理地址");
          execute.lookPhysicalAddress();                //调用方法查看物理地址
          System.out.println("4.启动万能五笔程序");
          execute.openWNWB();                           //调用方法启动万能五笔程序
      }
}

(3)运行结果如下所示:

1.查看目录
  驱动器 F 中的卷没有标签。
  卷的序列号是 9038-549A
  F:\ 的目录
2009-04-01 11:07                857 123.rar
2009-04-01 14:27             37,025 chuang.PNG
2009-03-24 16:12    <DIR>           delevopSpace
2009-04-01 09:01              1,104 poem.jar
2009-04-01 09:00                300 poem.txt
2009-04-01 15:45             13,824 poem.xls
2009-04-01 13:13                 52 poem2.txt
2009-04-01 15:53                333 poems.txt
2009-04-03 08:43    <DIR>           temp
2009-04-01 12:22    <DIR>           WinRAR
2009-04-02 16:06    <DIR>           个人资源
2009-04-02 17:59    <DIR>           开发文档
2009-03-26 09:10    <DIR>           数据
                  7 个文件         53,495 字节
                  6 个目录 113,287,254,016 可用字节
2.设置查看环境变量
系统变量:USERPROFILE=.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH
系统变量:JAVA_HOME=C:
系统变量:TEMP=C:\Program Files
系统变量:Path=C:
系统变量:PROCESSOR_REVISION=Console
系统变量:USERDOMAIN=C:\Documents and Settings\All Users
系统变量:PROCESSOR_IDENTIFIER=Console
系统变量:TMP=value2
系统变量:PROCESSOR_ARCHITECTURE=::\
系统变量:CLASSPATH=C:\Program Files\Common Files
系统变量:LOGONSERVER=Windows_NT
系统变量:FP_NO_HOST_CHECK=\Documents and Settings\Administrator
系统变量:PROCESSOR_LEVEL=ZHANGFAN
系统变量:SystemRoot=C:\WINDOWS
系统变量:NUMBER_OF_PROCESSORS=Administrator
系统变量:ComSpec=C:\Documents and Settings\Administrator\Application Data
3.查看物理地址
物理地址:00-21-27-08-84-92
物理地址:00-1F-D0-13-C3-C6
物理地址:00-21-27-08-84-92
4.启动万能五笔程序

源程序解读

(1)TextCMDExecute类的lookDirectory()方法用来查看目录。根据DOC命令dir创建进程管理实例。进程管理实例的directory()方法用来查看所指定的目录,start()方法创建启动的进程对象。进程的getInputStream()方法获得进程中的输入流,根据输入流创建输入读流并设置其编码格式为GBK,再根据输入读流创建缓冲读对象,利用读取的每行数据不为空进行循环,将数据输出到控制台。

(2)setEnvironment()方法用来设置环境变量。根据DOC执行窗口创建进程管理实例,进程管理实例的environment()方法用来获取系统参数,put()方法用来设置环境变量,keySet()方法获取Map集合中的键值,iterator()方法创建迭代器。运用迭代器的hasNext()方法进行循环,遍历显示集合中的系统变量。directory(File参数)用来设置工作目录,start()方法用来启动进程。

(3)lookPhysicalAddress()方法用来查看计算机的物理地址(MAC地址)。根据DOC命令ipconfig/all创建进程管理实例并根据其start()方法创建、启动进程。进程的getInputStream()方法获得输入流。循环读取输入流中的信息,将读取的信息写入缓冲字符串中,读取完毕后关闭相应的流资源。将缓冲字符串转化为字符串,运用字符串的indexOf()方法获得指定字符串的下标。循环根据下标利用substring()方法截取指定的字符串,截取的字符串即为物理地址。其中substring(1, 18)方法中,返回一个新字符串,它是此字符串的一个子字符串。该子字符串从指定的1(下标或索引)处开始,一直到索引18-1处的字符。因此,该子字符串的长度为18-1。

(4)openWNWB()方法用来启动万能五笔程序。其根据指定的可执行程序创建进程管理实例,调用其start()方法用来启动进程。

实例102 监控管理虚拟机

Java对服务器级机器上运行的虚拟机的垃圾收集性能作了优化调整,本实例介绍如何对Java虚拟机进行监控与管理,包括查看Java虚拟机的线程数、运行时间、内存使用情况以及垃圾回收情况。

技术要点

实现监控管理虚拟机的技术要点如下:

• java.lang.management.ManagementFactory是管理Bean的工厂类,通过它的get系列方法能够获得不同的管理Bean的实例。

• java.lang.management.MemoryMXBean:该Bean用于管理Java虚拟机的内存系统。一个Java虚拟机具有一个实例。

• java.lang.management.ClassLoadingMXBean:该Bean用于管理Java虚拟机的类加载系统。一个Java虚拟机具有一个实例。

• java.lang.management.ThreadMXBean:该Bean用于管理Java虚拟机的线程系统。一个Java虚拟机具有一个实例。

• java.lang.management.RuntimeMXBean:该Bean用于管理Java虚拟机的运行时系统。一个Java虚拟机具有一个实例。

• java.lang.management.OperatingSystemMXBean:该Bean用于管理操作系统。一个Java虚拟机具有一个实例。

• java.lang.management.CompilationMXBean:该Bean用于管理Java虚拟机的编译系统。一个Java虚拟机具有一个实例。

• java.lang.management.GarbageCollectorMXBean:该Bean用于管理Java虚拟机的垃圾回收系统。一个Java虚拟机具有一个或者多个实例。

实现步骤

(1)新建一个类名为TextControlJVM.java。

(2)代码如下所示:

package com.zf.s11;                                    //创建一个包
import java.lang.management.ClassLoadingMXBean;        //引入类
import java.lang.management.CompilationMXBean;
import java.lang.management.GarbageCollectorMXBean;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.OperatingSystemMXBean;
import java.lang.management.RuntimeMXBean;
import java.lang.management.ThreadMXBean;
import java.util.List;
public class TextControlJVM {                   //操作通过MXBean可以管理和监视虚拟机类
    //操作Java虚拟机的内存系统的管理接口MemoryMXBean
    public static void showMemoryMXBean(){
          MemoryMXBean instance = ManagementFactory
              .getMemoryMXBean();               //获得单一实例
          System.out.printf("%s%n",
                instance.getClass().getName()); //获得MemoryMXBean的类名称
          System.out.printf("%s: %s%n", "HeapMemoryUsage",
              instance.getHeapMemoryUsage());   //显示用一对象分配的堆的当前内存使用量
          System.out.printf("%s: %s%n", "getNonHeapMemoryUsage",
              instance.getNonHeapMemoryUsage()); //返回Java虚拟机使用非堆内存当前使用量
          instance.gc();                         //运行垃圾回收器
    }
    //操作Java虚拟机的类加载系统的管理接口ClassLoadingMXBean
    public static void showClassLoadingMXBean(){
          ClassLoadingMXBean instance = ManagementFactory
                    .getClassLoadingMXBean();    //获得单一实例
          System.out.printf("%s%n",              //获得ClassLoadingMXBean的类名称
                    instance.getClass().getName());
          System.out.printf("%s: %s%n", "LoadedClassCount",
                    instance.getLoadedClassCount());//返回当前加载到Java虚拟机中的类数量
          //返回自Java虚拟机开始执行到目前已经加载的类的总数
          System.out.printf("%s: %s%n", "TotalLoadedClassCount",
                    instance.getTotalLoadedClassCount());
          //返回自Java虚拟机开始执行到目前已经卸载的类的总数
          System.out.printf("%s: %s%n", "UnloadedClassCount",
                    instance.getUnloadedClassCount());
    }
    //操作Java 虚拟机线程系统的管理接口ThreadMXBean
    public static void showThreadMXBean() {
          ThreadMXBean instance = ManagementFactory
                    .getThreadMXBean();           //获得单一实例
          System.out.printf("%s%n",
                    instance.getClass().getName());     //获得ThreadMXBean的类名称
          //返回活动线程的当前数目,包括守护线程和非守护线程
          System.out.printf("%s: %s%n", "ThreadCount",
                    instance.getThreadCount());
          System.out.printf("%s: %n", "Thread IDs");   //返回活动线程 ID
          long[] ids = instance.getAllThreadIds();     //将返回的活动线程ID放到数组中
          for (long id : ids) {                   //循环数组显示活动线程ID
              System.out.printf("%s; ", id);
          }
          System.out.println();
          System.out.printf("%s: %s%n", "DaemonThreadCount",
                    instance.getDaemonThreadCount());   //返回活动守护线程的当前数目
          //返回自从Java虚拟机启动或峰值重置以来峰值活动线程计数
          System.out.printf("%s: %s%n", "PeakThreadCount",
                    instance.getPeakThreadCount());
          System.out.printf("%s: %s%n", "CurrentThreadCpuTime",
                    instance.getCurrentThreadCpuTime()); //返回当前线程的总 CPU 时间
          //返回当前线程在用户模式中执行的 CPU 时间
          System.out.printf("%s: %s%n", "CurrentThreadUserTime",
                    instance.getCurrentThreadUserTime());
      }
      //操作Java 虚拟机的运行时系统的管理接口RuntimeMXBean
      public static void showRuntimeMXBean() {
          RuntimeMXBean instance = ManagementFactory
                    .getRuntimeMXBean();           //获得单一实例
          System.out.printf("%n%s%n",
                    instance.getClass().getName());      //获得RuntimeMXBean的类名称
          //返回由引导类加载器用于搜索类文件的引导类路径
          System.out.printf("%s: %s%n", "BootClassPath",
                    instance.getBootClassPath());
          //返回系统类加载器用于搜索类文件的 Java 类路径
          System.out.printf("%s: %s%n", "ClassPath",
                    instance.getClassPath());
          System.out.printf("%s%n", "InputArguments");
          List<String> args = instance.getInputArguments();//获得JVM输入参数放入列表
          for (String arg : args) {                //循环显示输入参数
              System.out.printf("%s; ", arg);
          }
          System.out.printf("%s: %s%n", "LibraryPath",
                    instance.getLibraryPath());    //返回Java库路径
          //返回正在运行的 Java 虚拟机实现的管理接口的规范版本
          System.out.printf("%s: %s%n", "ManagementSpecVersion",
                    instance.getManagementSpecVersion());
          System.out.printf("%s: %s%n", "Name",
                    instance.getName());           //返回表示正在运行的 Java 虚拟机名称
          System.out.printf("%s: %s%n", "SpecName",
                    instance.getSpecName());       //返回 Java 虚拟机规范名称
          System.out.printf("%s: %s%n", "SpecVendor",
                    instance.getSpecVendor());     //返回 Java 虚拟机规范供应商
          System.out.printf("%s: %s%n", "SpecVersion",
                    instance.getSpecVersion());    //返回 Java 虚拟机规范版本
          System.out.printf("%s: %s%n", "VmName",
                    instance.getVmName());         //返回 Java 虚拟机实现名称
          System.out.printf("%s: %s%n", "VmVendor",
                    instance.getVmVendor());       //返回 Java 虚拟机实现供应商
          System.out.printf("%s: %s%n", "VmVersion",
                    instance.getVmVersion());      //返回 Java 虚拟机实现版本
          System.out.printf("%s: %s%n", "StartTime",
                    instance.getStartTime());      //返回 Java 虚拟机的启动时间
          System.out.printf("%s: %s%n", "Uptime",
                    instance.getUptime());         //返回 Java 虚拟机的正常运行时间
      }
      //操作Java操作系统的管理接口OperatingSystemMXBean
      public static void showOperatingSystemMXBean() {
          OperatingSystemMXBean instance = ManagementFactory
                    .getOperatingSystemMXBean();   //获得单一实例
          System.out.printf("%s%n",                //返回操作系统的架构
                    instance.getClass().getName());//获得OperatingSystemMXBean类名称
          System.out.printf("%s: %s%n", "Arch",
                    instance.getArch());           //返回Java虚拟机可以使用的处理器数目
          System.out.printf("%s: %s%n", "AvailableProcessors", instance
                    .getAvailableProcessors());
          System.out.printf("%s: %s%n", "Name",
                    instance.getName());           //返回操作系统名称
          System.out.printf("%s: %s%n", "Version",
                    instance.getVersion());        //返回操作系统版本
      }
      //操作Java 虚拟机的编译系统的管理接口CompilationMXBean
      public static void showCompilationMXBean() {
          CompilationMXBean instance = ManagementFactory
                    .getCompilationMXBean();       //获得单一实例
          System.out.printf("%n%s%n",              //获得CompilationMXBean的类名称
                    instance.getClass().getName());
          System.out.printf("%s: %s%n", "Name",
                    instance.getName());           //返回即时 (JIT) 编译器的名称
          System.out.printf("%s: %s%n", "TotalCompilationTime",
                    instance.getTotalCompilationTime());//返回编译上花费的累积耗费时间近似值
      }
      //操作Java 虚拟机的垃圾回收的管理接口GarbageCollectorMXBean
      public static void showGarbageCollectorMXBean() {
          List<GarbageCollectorMXBean> instances = ManagementFactory
                    .getGarbageCollectorMXBeans(); //获得所有实例
          System.out.printf("%s%n", GarbageCollectorMXBean.class
                    .getName());                   //获得GarbageCollectorMXBean类名称
          for (GarbageCollectorMXBean instance : instances) {//遍历每个实例
              System.out.printf("***%s: %s***%n", "Name",
                        instance.getName());       //返回垃圾收集器的名字
              System.out.printf("%s: %s%n", "CollectionCount",
                        instance.getCollectionCount()); //返回已发生的回收的总次数
              System.out.printf("%s: %s%n", "CollectionTime",
                        instance.getCollectionTime());  //返回近似的累积回收时间
          }
      }
      public static void main(String[] args) {    //Java程序主入口处
          System.out.println("1.显示Java虚拟机的内存系统的管理接口MemoryMXBean信息");
          showMemoryMXBean();                     //调用方法
          System.out.println("2.显示Java虚拟机的类加载系统的管理接口ClassLoadingMXBean信息");
          showClassLoadingMXBean();               //调用方法
          System.out.println("3.显示Java虚拟机线程系统的管理接口ThreadMXBean信息");
          showThreadMXBean();                     //调用方法
          System.out.println("4.显示Java虚拟机的运行时系统的管理接口RuntimeMXBean信息");
          showRuntimeMXBean();                    //调用方法
          System.out.println("5.显示Java操作系统的管理接口OperatingSystemMXBean信息");
          showOperatingSystemMXBean();//调用方法
          System.out.println("6.显示Java虚拟机的编译系统的管理接口CompilationMXBean信息");
          showCompilationMXBean();                //调用方法
          System.out.println("7.显示Java虚拟机的垃圾回收的管理接口GarbageCollectorMXBean信息");
          showGarbageCollectorMXBean();           //调用方法
      }
}

(3)运行结果如下所示:

1.显示Java虚拟机的内存系统的管理接口MemoryMXBean信息
sun.management.MemoryImpl
HeapMemoryUsage: init = 0(0K) used = 481112(469K) committed = 5177344(5056K)
max = 66650112(65088K)
getNonHeapMemoryUsage: init = 33718272(32928K) used = 13075544(12769K)
committed = 34078720(33280K) max = 121634816(118784K)
2.显示Java虚拟机的类加载系统的管理接口ClassLoadingMXBean信息
sun.management.ClassLoadingImpl
LoadedClassCount: 496
TotalLoadedClassCount: 496
UnloadedClassCount: 0
3.显示Java虚拟机线程系统的管理接口ThreadMXBean信息
sun.management.ThreadImpl
ThreadCount: 5
Thread IDs:
5; 4; 3; 2; 1;
DaemonThreadCount: 4
......
4.显示Java 虚拟机的运行时系统的管理接口RuntimeMXBean信息
sun.management.RuntimeImpl
BootClassPath:
E:\TOOL\jre\lib\resources.jar;E:\TOOL\jre\lib\rt.jar;
E:\TOOL\jre\lib\sunrsasign.jar;E:\TOOL\jre\lib\js......
5.显示Java操作系统的管理接口OperatingSystemMXBean信息
com.sun.management.OperatingSystem
Arch: x86
AvailableProcessors: 2
Name: Windows XP
Version: 5.1
6.显示Java 虚拟机的编译系统的管理接口CompilationMXBean信息
sun.management.CompilationImpl
Name: HotSpot Client Compiler
TotalCompilationTime: 18
7.显示Java 虚拟机的垃圾回收的管理接口GarbageCollectorMXBean信息
java.lang.management.GarbageCollectorMXBean
......

源程序解读

(1)showMemoryMXBean()方法使用MemoryMXBean类管理Java虚拟机的内存系统。ManagementFactory的getMemoryMXBean()方法获得MemoryMXBean类实例。getHeapMemoryUsage()方法返回Java JVM(虚拟机)用于对象分配的堆的当前内存使用量;getNonHeapMemoryUsage()方法返回Java虚拟机使用的非堆内存的当前使用量;gc()方法运行垃圾回收器。

(2)showClassLoadingMXBean()方法使用ClassLoadingMXBean类管理Java虚拟机的类加载系统。ManagementFactory类的getClassLoadingMXBean()方法获得ClassLoadingMXBean类的实例。getLoadedClassCount()方法返回当前加载到Java虚拟机中的类的数量;getTotalLoadedClassCount()方法返回自Java虚拟机开始执行到当前已经加载的类的总数;getUnloadedClassCount()方法返回自Java虚拟机开始执行到当前已经卸载的类的总数。

(3)showThreadMXBean()方法使用ThreadMXBean管理Java虚拟机的线程系统。ManagementFactory类的getThreadMXBean()方法获得ThreadMXBean类的实例。getThreadCount()方法返回活动线程的当前数目,包括守护线程和非守护线程;getAllThreadIds()返回所有线程的ID;getDaemonThreadCount()方法获得所有守护线程的数目;getPeakThreadCount()方法返回自从Java虚拟机启动或峰值重置以来峰值活动线程计数;getCurrentThreadCpuTime()方法返回当前线程的总CPU时间;getCurrentThreadUserTime()方法返回当前线程在用户模式中执行的CPU时间。

(4)showRuntimeMXBean()方法使用RuntimeMXBean类管理Java虚拟机的运行时系统。ManagementFactory类的getRuntimeMXBean()方法获得RuntimeMXBean类的实例。getBootClassPath()方法返回由引导类加载器用于搜索类文件的引导类路径;getClassPath()方法返回系统类加载器用于搜索类文件的Java类路径;getInputArguments方法返回传递给Java虚拟机的输入变量,其中不包括传递给main方法的变量;getLibraryPath()方法返回Java类库路径;getName()方法获得Java虚拟机的名字;getSpecName()、getSpecVendor()和getSpecVersion()方法获得Java虚拟机规范的名称、供应商和版本;getVmName()、getVmVendor()和getVmVersion()方法获得Java虚拟机实现的名称、供应商和版本;getStartTime()方法获得Java虚拟机的启动时间;getUptime()方法获得Java虚拟机的正常运行时间。

(5)showOperatingSystemMXBean()方法使用OperatingSystemMXBean类管理操作系统。ManagementFactory类的getOperatingSystemMXBean()方法获得OperatingSystemMXBean的实例。getArch()方法返回操作系统的架构;getAvailableProcessors()方法返回Java虚拟机可以使用的处理器数目;getName()、getVersion()方法返回操作系统的名称和版本。

(6)showCompilationMXBean()方法使用CompilationMXBean类管理Java虚拟机的编译系统。ManagementFactory类的getCompilationMXBeann()方法获得CompilationMXBean类的实例。getName()方法返回即时(JIT)编译器的名称;getTotalCompilationTime()方法返回在编译上花费的累积耗费时间的近似值。

(7)showGarbageCollectorMXBean()方法使用GarbageCollectorMXBean类管理Java虚拟机的垃圾回收系统。ManagementFactory类的getGarbageCollectorMXBean方法获得GarbageCollectorMXBean类的实例。getName()方法返回垃圾收集器的名字;getCollectionCount()方法返回已发生的垃圾回收的总次数;getCollectionTime()方法返回近似的累积回收时间。