本章章节
> 5.1 面向对象程序设计的基本概念
> 5.2类与对象
> 5.3类的封装性
> 5.4在类内部调用方法
> 5.5引用数据类型的传递
> 5.6构造方法
> 5.7匿名对象
> 5.8对象的比较
> 5.9 this 关键字的使用
> 5.10 static 关键字的使用
> 5.11构造方法的私有
> 5.12对象数组的使用
> 5.13 Java基本数据类型的包装类
> 5.14 String类的常见方法
> 5.15 Java文档注释
.本章摘要:
5.10 static 关键字的使用
5.10.1 静态变量
在程序中如果用static 声明变量的话,则此变量称为静态变量。那么什么是静态变量?使用静态变量又有什么好处呢?先来看一下下面的范例:
范例:TestStaticDemo1.java
class Person { String name; String city; int age; public Person(String name, String city, int age) { this.name = name; this.city = city; this.age = age; } public String talk() { return "我是:" + this.name + ",今年:" + this.age + "岁,来自:" + this.city; } } public class TestStaticDemo1 { public static void main(String[] args) { Person p1 = new Person("张三", 25, "中国"); Person p2 = new Person("李四", 30, "中国"); Person p3 = new Person("王五", 35, "中国"); System.out.println(p1.talk()); System.out.println(p2.talk()); System.out.println(p3.talk()); } }
输出结果:
我是:张三,今年:25岁,来自:中国
我是:李四,今年:30岁,来自:中国
我是:王五,今年:35岁,来自:中国
程序说明:
1、程序1~16行声明一个名为Person的类,含有三个属性:name、age、city。
2、程序6~11行声明Person类的一个构造方法,此构造方法分别为各属性赋值。
3、程序12~15行声明一个talk()方法,此方法用于返回用户信息。
4、程序21~26行分别实例化三个Person对象并调用类中的talk()方法,输出用户信息。
由上面的程序可以发现,所有的Person对象都有一个city属性,而且值也全部相同,如图5-13所示:
图5-13 类结构图
可以试着想一想,现在假设程序产生了50个Person对象,如果想修改所有人的city属性的话,是不是就要调用50遍city属性,进行重新修改,显然太麻烦了。所以在java中提供了static关键字,用它来修饰类的属性后,则此属性就是公共属性了(或称为类属性)。
访问static成员变量的方式:
对象.成员变量
或
类名.成员变量
将TestStaticDemo1.java程序稍作修改就形成了范例TestStaticDemo2.java,如下所示:
范例:TestStaticDemo2.java
class Person { String name; static String city = "中国"; int age; public Person(String name, int age) { this.name = name; this.age = age; } public String talk() { return "我是:" + this.name + ",今年:" + this.age + "岁,来自:" + city; } } public class TestStaticDemo2 { public static void main(String[] args) { Person p1 = new Person("张三", 25); Person p2 = new Person("李四", 30); Person p3 = new Person("王五", 35); System.out.println("修改之前信息:" + p1.talk()); System.out.println("修改之前信息:" + p2.talk()); System.out.println("修改之前信息:" + p3.talk()); System.out.println(" ************* 修改之后信息**************"); // 修改后的信息 p1.city = "美国";
System.out.println("修改之后信息:" + p1.talk()); System.out.println("修改之后信息:" + p2.talk()); System.out.println("修改之后信息:" + p3.talk()); } }
输出结果:
修改之前信息:我是:张三,今年:25岁,来自:中国
修改之前信息:我是:李四,今年:30岁,来自:中国
修改之前信息:我是:王五,今年:35岁,来自:中国
************* 修改之后信息**************
修改之后信息:我是:张三,今年:25岁,来自:美国
修改之后信息:我是:李四,今年:30岁,来自:美国
修改之后信息:我是:王五,今年:35岁,来自:美国
程序说明:
1、程序1~15行声明一个名为Person的类,含有三个属性:name、age、city。其中city为static类型。
2、程序6~10行声明Person类的一个构造方法,此构造方法作用是分别为name、age属性赋值。
3、程序11~14行声明一个talk()方法,此方法用于返回用户信息。
4、程序20~22行分别实例化三个Person对象。
5、程序23~25行分别调用类中的talk()方法,输出用户信息。
6、程序28行修改了p1中的city属性。
从上面的程序中可以发现,程序只在第28行修改了city属性,而且只修改了一个对象的city属性,但再次输出时,发现全部的对象的city值都发生了一样的变化,这就说明了用static声明的属性是所有对象共享的。如下图5-14所示:
图5-14 static变量的使用
在图中可以发现,所有的对象都指向同一个city属性,只要当中有一个对象修改了city属性的内容,则所有对象都会被同时修改。
另外,需要注意一点,用static方式声明的属性,也可以用类名直接访问,拿上面的程序来说,如果想修改city属性中的内容,可以用如下方式:
Person.city = "美国";
所以把用static类型声明的变量,称为“类变量”。即用static声明的变量是属于类的,而不是属于具体的某个对象。
小提示:
既然static类型的变量是所有对象共享的内存空间,也就是说无论最终有多少个对象产生,也都只有一个static类型的属性,那可不可以用它来计算类到底产生了多少个实例对象呢?可以想一想,只要一个类产生一个新的实例对象,都会去调用构造方法,所以可以在构造方法中加入一些记数操作,如下面的程序:
class Person { static int count = 0; // 声明一个static类型的整型变量 public Person() { count++; // 增加了一个对象 System.out.println("产生了:" + count + "个对象!"); } } public class TestStaticDemo3 { public static void main(String[] args) { new Person(); new Person(); } }
5.10.2 静态方法
static既可以在声明变量时使用,也可以用其来声明方法,用它声明的方法有时也被称为“类方法”,请看下面的范例:
范例:TestStaticDemo4.java
class Person { String name; private static String city = "中国"; int age; public Person(String name, int age) { this.name = name; this.age = age; } public String talk() { return "我是:" + this.name + ",今年:" + this.age + "岁,来自:" + city; } public static void setCity(String c) { city = c; } } public class TestStaticDemo4 { public static void main(String[] args) { Person p1 = new Person("张三", 25); Person p2 = new Person("李四", 30); Person p3 = new Person("王五", 35); System.out.println("修改之前信息:" + p1.talk()); System.out.println("修改之前信息:" + p2.talk()); System.out.println("修改之前信息:" + p3.talk()); System.out.println(" **************修改之后信息**************"); // 修改后的信息 Person.setCity("美国"); System.out.println("修改之后信息:" + p1.talk()); System.out.println("修改之后信息:" + p2.talk()); System.out.println("修改之后信息:" + p3.talk()); } }
输出结果:
修改之前信息:我是:张三,今年:25岁,来自:中国
修改之前信息:我是:李四,今年:30岁,来自:中国
修改之前信息:我是:王五,今年:35岁,来自:中国
**************修改之后信息**************
修改之后信息:我是:张三,今年:25岁,来自:美国
修改之后信息:我是:李四,今年:30岁,来自:美国
修改之后信息:我是:王五,今年:35岁,来自:美国
程序说明:
1、程序1~19行声明一个名为Person的类,类中含有一个static类型的变量city,并对此对象进行了封装。
2、15~18行声明一个static 类型的方法,此方法也可以用类名直接调用,用于修改city属性的内容。
3、程序第32行由Person调用setCity()方法,对city的内容进行修改。
注意:
在使用static类型声明的方法时需要注意的是:如果在类中声明了一个static类型的属性,则此属性既可以在非static类型的方法中使用,也可以在static类型的方法中使用。但在static类型修饰的方法中调用非static类型的属性或方法时,则会出现错误。静态方法不能被覆盖成非静态。同样,非静态方法也不能被覆盖成静态方法。
public class PersonStatic { String name = "张三"; static String city = "中国"; int age; public PersonStatic(String name, int age) { this.name = name; this.age = age; } public void fun() { } public static void print() { fun(); //fun为非static方法 出错 System.out.println(name); //name为非static变量 出错 } public String talk() { return "我是:" + this.name + ",今年:" + this.age + "岁,来自:" + city; } }
可以发现,程序中print()方法是static类型的,而name属性和fun方法都是非static类型的,所以print在调用非static类型的name属性和非static类型的fun方法时就出现了错误。解决办法是:传递一个本类的对象进去,通过对象去访问。如下:
class Person { String name; static String city = "中国"; int age; public Person(String name, int age) { this.name = name; this.age = age; } public void fun() { } public static String talk(Person p) { p.fun(); return "我是:" + p.name + ",今年:" + p.age + "岁,来自:" + p.city; } } public class TestJava2_1 { public static void main(String[] args) { Person p1 = new Person("张三", 25); Person p2 = new Person("李四", 30); Person p3 = new Person("王五", 35); System.out.println("修改之前信息:" + p1.talk(p1)); System.out.println("修改之前信息:" + p2.talk(p2)); System.out.println("修改之前信息:" + p3.talk(p3)); System.out.println(" ************* 修改之后信息**************"); // 修改后的信息 Person.city = "美国"; System.out.println("修改之后信息:" + p1.talk(p1)); System.out.println("修改之后信息:" + p2.talk(p2)); System.out.println("修改之后信息:" + p3.talk(p3)); } }
5.10.3 理解 main()方法
在前面的章节中,已经多次看到,如果一个类要被Java解释器直接装载运行,这个类中必须有main()方法。有了前面所有的知识,现在可以理解main()方法的含义了。
由于Java虚拟机需要调用类的main()方法,所以该方法的访问权限必须是public,又因为Java虚拟机在执行main()方法时不必创建对象,所以该方法必须是static的,该方法接收一个String类型的数组参数,该数组中保存执行Java命令时传递给所运行的类的参数。
向java中传递参数可用如下的命令:
java 类名称 参数1 参数2 参数3
通过运行程序TestMain.java来了解如何向类中传递参数以及程序又是如何取得这些参数的,如下所示:
范例:TestMain.java
public class TestMain { /** * public:表示公共方法 * <p> * static:表示此方法为一静态方法,可以由类名直接调用 * <p> * void:表示此方法无返回值 * <p> * main:系统定义的方法名称 * <p> * String args[]:接收运行时参数 */ public static void main(String[] args) { int j = args.length; // 取得输入参数的长度 if (j != 2) { System.out.println("输入参数个数有错误!"); System.exit(1); // 退出程序 } for (int i = 0; i < args.length; i++) { System.out.println(args[i]); } } }
运行程序:
java TestMain first second
输出结果:
first
second
程序说明:
1、程序第13行判断输入参数的个数是否为两个参数,如果不是,则退出程序。
2、所有的接收的参数都被存放在args[]字符串数组之中,用for循环输出全部内容。
需要注意的是,Java里面的参数传递,args[0]已经是传递进来的参数了,而C/C++里面的argv[0]表示程序本身,即Java里面args是不包含可执行程序本身的。
5.10.4 静态代码块
一个类可以使用不包含在任何方法体中的静态代码块,当类被载入时,静态代码块被执行,且只执行一次,静态代码块经常用来进行类属性的初始化。如下面的程序代码所示:
范例:TestStaticDemo5.java
class Person { public Person() { System.out.println("1.public Person()"); } // 此段代码会优先构造函数执行 static { System.out.println("2.Person类的静态代码块被调用!"); } } public class TestStaticDemo5 { // 运行本程序时,静态代码块会被自动执行 static { System.out.println("3.TestStaticDemo5类的静态代码块被调用!"); } public static void main(String[] args) { System.out.println("4.程序开始执行!"); // 产生两个实例化对象 new Person(); new Person(); } }
输出结果:
3.TestStaticDemo5类的静态代码块被调用!
4.程序开始执行!
2.Person类的静态代码块被调用!
1.public Person()
1.public Person()
程序说明:
1、程序1~12行声明一个名为Person的类。
2、程序8~11行声明一个静态代码块,此代码块放在Person类之中。
3、程序15~18行在类TestStaticDemo5中也声明一静态代码块。
4、22、23行分别产生了两个Person类的匿名对象。
从程序运行结果中可以发现,放在TestStaticDemo5类中的静态代码块首先被调用,这是因为程序首先执行TestStaticDemo5类,所以此程序的静态代码块会首先被执行。程序在22、23行产生了两个匿名对象,可以发现Person类中的静态代码块只执行了一次,而且静态代码块优先于静态方法和构造函数,由此可以发现,静态代码块可以为静态属性初始化。
java中存在四种代码块:
1、普通代码块:是直接写在各个方法中的代码块。
2、构造代码块:是直接写在类中的代码块,在实例化对象的时候调用,而且每实例化一个对象就要调用一次构造代码块(可多次调用)。构造代码块优先于构造方法执行。
3、静态代码块:是使用static声明的代码块,静态块只调用一次,而且优先于构造代码块执行。作用:为static类型的属性初始化。
4、同步代码块:主要出现在多线程中。
代码块的执行顺序及执行次数:
1、主方法(main)中的静态代码块优先于主方法执行;
2、主方法(main)优先于其他类中的静态代码块执行;
3、在普通类中定义的静态代码块,优先于构造代码块执行;
4、父类的静态代码块优先于子类的静态代码块执行;
5、子类的静态代码块优先于父类的构造代码块执行;
6、不管有多少个实例化对象产生,静态代码块只执行一次;
7、父类的构造代码块和构造方法,优先于子类的构造代码块;
8、构造代码块优先于构造方法执行。
9、构造代码块和构造方法执行的次数与声明的对象数相等,父类同样。
实例:各代码块执行的优先顺序
class SuperClass { //父类 static
{ System.out.println("1、父类static代码块"); } { System.out.println("2、父类构造代码块"); } public SuperClass() { System.out.println("3、父类构造方法"); } } class SunClass extends SuperClass { //子类 static { System.out.println("4、子类static代码块"); } { System.out.println("5、子类构造代码块"); } public SunClass() { System.out.println("6、子类构造方法"); } } public class Test { static { System.out.println("7、主类static代码块"); } { System.out.println("8、主类代码块"); } public static void main(String[] args) { // TODO Auto-generated method stub System.out.println("9、主方法开始执行"); SunClass sc1 = new SunClass(); SunClass sc2 = new SunClass(); SunClass sc3 = new SunClass(); System.out.println("10、主方法执行结束"); } }
5.11 构造方法的私有
方法依实际需要,可分为public与private。同样的,构造方法也有public与private之分。到目前为止,所使用的构造方法均属于public,它可以在程序的任何地方被调用,所以新创建的对象也都可以自动调用它。如果构造方法被设为private,则无法在该构造方法所在的类以外的地方被调用。请看下面的程序:
范例:TestSingleDemo1.java
public class TestSingleDemo1 { private TestSingleDemo1() { System.out.println("private TestSingleDemo1 ."); } public static void main(String[] args) { new TestSingleDemo1(); } }
输出结果:
private TestSingleDemo1 .
由上面程序可以发现,程序第3行在声明构造方法的时候将之声明为private类型,则此构造方法只能在本类内被调用。同时你会发现,而在本程序中的main方法也放在TestSingleDemo1类的内部,所以在本类中可以自己产生实例化对象。
看过上面的程序,这么做似乎没有什么意义,因为一个类最终都会由外部去调用,如果这么做的话,岂不是所有构造方法被私有化了的类都需要这么去调用了吗?那程序岂不是会有很多的main()方法了吗?举这个例子主要是让大家清楚:构造方法虽然被私有了,但并不一定是说此类不能产生实例化对象,只是产生这个实例化对象的位置有所变化,即只能在本类中产生实例化对象。请看下面的范例:
范例:TestSingleDemo2.java
class Person { String name; // 在本类声明Person对象p,注意此对象用final标记,表示不能再重新实例化 private static final Person p = new Person(); private Person() { name = "张三"; } public static Person getP() { return p; } } public class TestSingleDemo2 { public static void main(String[] args) { // 声明一个Person类的对象 Person p = Person.getP(); System.out.println(p.name); } }
输出结果:
张三
程序说明:
1、程序6~9行将Person类的构造方法封装起来,外部无法通过其构造方法产生实例化对象。
2、程序第5行声明一个Person类的实例化对象,此对象是在Person类内部实例化,所以可以调用私有构造方法。另外,此对象被标识为static 类型,表示为静态属性,同时此对象被私有化,另外在声明Person对象的时候加上了一个final 关键字,此关键字表示Person的对象p不能被重新实例化。(关于final后面会详细介绍)
3、程序10~13定义了一个静态函数,将类内部实例化的对象传出。因为构造函数是私有的,所以在外部不能再实例化对象,即不能通过“对象.getP()”的形式调用getP,所以getP需要声明为static的。表示属于类方法,可以利用“类名.getP()”的形式来调用
4、程序第21行声明一个Person类的对象p,调用Person类中的getP()方法,此方法返回Person类的实例化对象。
从上面程序中可以发现,无论在Person类的外部声明多少个对象,最终得到的都是同一个引用,因为此类只能产生一个实例对象,这种做法在设计模式中称为单态模式。而所谓设计模式也就是在大量的实践中总结和理论化之后优选的代码结构、编程风格以及解决问题的思考方式。有兴趣可以研究一下。
如果没有加上final修饰,则Person p1 = new Person("张三", 25);之后,还可以利用p1 = new Person("李四", 33);来重新实例化。加上final修饰之后,则表面p1是最终变量,不能再实例化。
单态模式实例:
class Person { private String name; private static Person p = null; private Person(String name) { this.name = name; } public static Person getP(String name) { if (p == null) p = new Person(name); return p; } public void disp() { System.out.println("name=" + this.name); } } public class TestSingleDemo { public static void main(String[] args) { // 声明Person类的对象 Person p1 = Person.getP("aaa"); Person p2 = Person.getP("bbb"); p1.disp(); p2.disp(); } }
两次结果都输出aaa。因为从始至终都只有一个实例化对象。p1和p2都是引用的p。对于单例设计模式,典型的应用就是有些游戏只允许登录一个客户端,不能在一台PC机上启动多个。
5.12 对象数组的使用
在前面的章节中已介绍过如何以数组来保存基本数据类型的变量。相同的,对象也可以用数组来存放,通过下面两个步骤来实现:
1、声明类类型的数组变量,并用new分配内存空间给数组。
2、用new产生新的对象,并分配内存空间给它
例如,要创建三个Person类类型的数组元素,可用下列的语法:
Person p[]; // 声明Person类类型的数组变量
p = new Person[3]; // 用new分配内存空间
创建好数组元素之后,便可把数组元素指向由Person类所产生的对象:
p[0] = new Person (); p[1] = new Person (); // 用new产生新的对象,并分配内存空间给它 p[2] = new Person ();
此时,p[0]、p[1]、p[2]是属于Person类类型的变量,它们分别指向新建对象的内存参考地址。当然也可以写成如下形式:
Person p[] = new Person[3]; // 创建对象数组元素,并分配内存空间
利用for 循环来完成对象数组内的初始化操作,此方式属于动态初始化:
for(int i=0;i<p.length;i++) { p[i] = new Person(); }
或者采用静态方式初始化对象数组:
Person p[] = {new Person(), new Person(), new Person()};
范例:TestObjectArray.java
class Person { String name; int age; public Person() { } public Person(String name, int age) { this.name = name; this.age = age; } public String talk() { return "我是:" + this.name + ",今年:" + this.age + "岁"; } } public class TestObjectArray { public static void main(String[] args) { Person p[] = { new Person("张三", 25), new Person("李四", 30), new Person("王五", 35) }; for (int i = 0; i < p.length; i++) { System.out.println(p[i].talk()); } } }
输出结果:
我是:张三,今年:25岁
我是:李四,今年:30岁
我是:王五,今年:35岁
程序说明:
1、程序第22~24行用静态声明方式声明了三个对象的Person类的对象数组。
2、程序第25~28行用for循环输出所有对象,并分别调用talk()方法打印信息。
5.13 Java 基本数据类型的包装类
在Java 的设计中提倡一种思想:“一切皆对象”,那么这样一来就出现了一个矛盾。从数据类型的划分中可以知道Java中的数据类型分为基本数据类型和引用数据类型。基本数据类型定义的变量怎么能够成为对象呢?此时,就需要将基本数据类型进行包装。将八种基本类型变为一个类的形式,这就是包装类的作用。这样就可以以对象的形式操作基本数据类型。
Java里面八种基本数据类型对应的包装类如表5-1所示:
表5-1 八种基本数据类型的包装类
装箱及拆箱的概念:
将基本数据类型变为包装类称为装箱;
将包装类的数据类型变为基本数据类型称为拆箱。
以Integer为例:
public class AA { public static void main(String args[]) { int x = 30; // 基本数据类型 Integer i = new Integer(x); // 装箱:将基本数据类型变为包装类 int temp = i.intValue(); // 拆箱:将一个包装类变为基本数据类型 } }
在JDK1.5之前对于程序本身来说包装类是不能直接进行“+、-、*、/、++、--”操作的。因为包装类是一个类。但是在JDK1.5版本之后,对程序的包装类功能进行了改变,增加了自动装箱及自动拆箱的功能,而且也可以使用包装类直接进行数字运算。
public class AA { public static void main(String args[]) { Integer i = 30; // 自动装箱成 Integer Float f = 30.3f; // 自动装箱成Float int x = i; // 自动拆箱为int float y = f; // 自动拆箱为float } }
在包装类中,实际上还存在一个最大的特点,就是可以将字符串变为指定的数据类型。包装类在实际中用得最多的还是在于字符串变为基本数据类型的操作上。例如:将一个全由数字组成的字符串变为一个int类型的数据。在Integer类中分别提供了以下的方法:
//1. 利用构造函数直接进行转换 Integer i = new Integer("123"); System.out.println(i.intValue()); //2. 利用parseInt函数转换 int j = Integer.parseInt("456"); System.out.println(j);
5.14 String 类的常见方法
通过查看API文档,下面是常用的方法:
public String(char[] value):直接将一个字符数组变为一个字符串
public String(char[] value,int offset,int count):将一个指定范围的字符数组变为字符串
public String(byte[] bytes):将一个byte数组变为字符串
public String(byte[] bytes,int offset,int length):将一个指定范围的byte数组变为字符串
public char[] toCharArray():把一个字符串变为字符数组
public char charAt(int index):从字符串中取出指定位置的字符
int compareTo(String anotherString) 按字典顺序比较两个字符串。如果字符串位于anotherString之前,返回一个负数;如果在其后面,返回一个正数;相等,返回0;
public byte[] getBytes():将一个字符串变为byte 数组
public int length():取得字符串长度
public int indexOf(String str):从开头查找指定字符串的位置
public int indexOf(String str,int fromIndex):从指定位置查找指定字符串的位置
public String trim():清除字符串左右两端的空格
public String substring(int beginIndex):从指定位置开始,一直取到尾进行字符串的截取
public String substring(int beginIndex,int end):指定截取字符串的开始点和结束点
public String[] split(String regex):按照指定的字符串对字符串进行拆分
public String toUpperCase():将一个字符串全部变为大写字母
public String toLowerCase():将一个字符串全部变为小写字母
public boolean startsWith(String prefix):判断是否以指定的字符串开头
public boolean endsWith(String suffix):判断是否以指定的字符串结尾
public boolean equals(String str):判断两个字符串内容是否相等
public boolean equalsIgnoreCase(String str):不区分大小写比较两个字符串是否相等
String replace(char oldChar, char newChar):返回一个新的字符串,它是通过用newChar替换此字符串中出现的所有oldChar得到的。
public String replaceAll(String regex,String replacement):字符串替换
public static String valueOf(XXX X):XXX为基本数据类型
有些时候,需要由较短的字符串构建字符串,例如按键或来自文件中的单词,采用字符串的连接方式到底此目的效率比较低。每次连接字符串时,都会构建一个新的String对象,可以利用Stringbuilder类避免这样的问题。
如果需要许多小段的字符串构建一个字符串,那么应该按照下列步骤来进行。
首先构造一个空的StringBuilder字符串构建器:
StringBuilder builder = new StringBuilder();
当每次需要添加一部分内容时,就调用append方法:
builder.append(ch); // append 一个字符
builder.append(str); // append 一个字符串
当需要构建字符串时就调用toString()方法,将可以得到一个字符串对象:
string completedStr = builder.toString();
在JDK1.5中引入了StringBuilder类,这个类的前身是StringBuffer类,StringBuffer类的效率较低,但允许采用多线程的方式执行添加或删除字符串的操作。如果所有字符串在一个单线程中编辑,则使用StringBuilder代替它,两个类的API是一样的。
java.lang.StringBuilder常见类如下:
StringBuilder():构造一个空的字符串构建器。
int length():返回字符串构建器字符的个数。
StringBuilder append(String str):追加一个字符串并返回this。
StringBuilder append(char ch):追加一个字符并返回this。
void setCharAt(int i, char c):设置下标i的值为c。
StringBuilder insert(int offset, String str):在offset位置插入一个字符串并返回this。
StringBuilder insert(int offset, char ch):在offset位置插入一个字符并返回this。
StringBuilder delete(int startIndex, int endIndex):删除[startIndex, endIndex)区间的元素
String toString():返回一个与构建器内容相同的字符串。
5.15 Java 文档注释
在前面已经提到过,Java支持三种形式的注释。前两种是//和/*…*/。第三种方式被称为文档注释。它以“/**”开始,以“*/”标志结束。文档注释提供将程序信息嵌入到程序中的功能。开发者可以使用javadoc工具将信息取出,然后转换为HTML文件。文档注释提供了编写程序文档的便利方式。
5.15.1 javadoc 标记
javadoc程序识别下列标记:
Tag标记意义
正如上面看到的那样,所有的文档标记都以“(@)”标志开始。在一个文档注释中,也可以使用其它的标准HTML标记。然而,一些标记(如标题)是不能使用的,因为它们会破坏由javadoc生成的HTML文件外观。
可以使用文档注释为类、接口、域、构造方法和方法提供文档。在所有这些情况中,文档注释必须紧接在被注释的项目之前。为变量做注释可以使用@see、@since、@serial、@serialField和@deprecated文档标记。为类做注释可以使用@see、@author、@since、@deprecated和@version 文档标记。为方法做注释可以用@see、@return、@param、@since、@deprecated、@throws、@serialData和@exception文档标记。而{@link}或{@docRoot}标记则可以用在任何地方。下面详细列出了每个标记的使用方法:
@ author
标记@author指定一个类的作者,它的语法如下:
@author description
其中,description通常是编写这个类的作者名字。标记@author只能用在类的文档中。在执行javadoc时,需要指定-author选项,才可将@author域包括在HTML文档中。
@deprecated
@deprecated标记指示不赞成使用一个类或是一个成员。建议使用@see标记指示程序员其他的正确的选择。其语法如下:
@deprecated description
其中description是描述所反对的信息。由@deprecated标记指定的信息由编译器识别,包括在生成的.class文件中,因此在编译Java源文件时,程序员可以得到这个信息。
@deprecated标记可以用于变量,方法和为类做注释的文档中。
{@docRoot}
{@docRoot}指定当前文档的根目录路径。
@exception
@exception标记描述一个方法的异常,其语法如下:
@exception exception-name explanation
其中,异常的完整名称由exception-name指定,explanation是描述异常是如何产生的字符串。@exception只用于为方法做注释的文档。
{@link}
{@link} 标记提供一个附加信息的联机超链接。其语法如下:
{@link name text}
其中,name是加入超链接的类或方法的名字,text是显示的字符串。
@param
@param标记注释一个方法的参数。其语法如下所示:
@param parameter-name explanation
其中parameter-name指定方法的参数名。这个参数的含义由explanation描述。
@param标记只用在为方法做注释的文档中。
@return
@return标记描述一个方法的返回值。其语法如下:
@return explanation
其中,explanation 描述方法返回值的类型和含义。@return 标记只用为方法做注释的文档中。
@see
@see标记提供附加信息的引用。最常见的使用形式如下所示:
@see anchor
@see pkg.class#member text
在第一种格式中,anchor是一个指向绝对或相对URL的超链接。第二种格式,pkg.class#member指示项目的名字,text是项目的文本显示。文本参数是可选的,如果不用,显示由pkg.class#membe指定的项目。成员名,也是可选的。因此,除了指向特定方法或者字段的引用之外,还可以指定一个引用指向一个包,一个类或是一个接口。名字可以是完全限定的,也可以是部分限定的。但是,成员名(如果存在的话)之前的点必须被替换成一个散列字符#。
@serial
@serial标记为默认的可序列化字段定义注释文档。其语法如下:
@serial description
其中,description是字段的注释。
@serialData
@serialData标记为writeObject( )或者writeExternal( )方法编写的数据提供文档。其语法如下:
@serialData description
其中,description 是数据的注释。
@serialField
@serialField标记为ObjectStreamField组件提供注释,其语法如下:
@serialField name type description
其中,name是域名,type是域的类型,description是域的注释。
@since
@since标记声明由一个特定发布版本引入的类或成员。其语法如下:
@since release
其中,release是指示这个特性可用的版本或发布的字符串。@since标记可以用在为变量、方法和类的做注释文档中。
@throws
@throws标记与@exception标记的含义相同。
@version
@version标记指示类的版本。其语法如下:
@version info
其中,info是包含版本信息的字符串,典型情况下是如2.2这样的版本号。@version标记只用在为类的做注释文档中。在执行javadoc时,指定-version选项,可将@version域包含在HTML文档中。
5.15.2 文档注释的一般形式
在用/**开头后,第一行,或者头几行是类、变量或方法的主要描述。其后,可以包括一个或多个不同的@标记。每个@标记必须在一个新行的开头,或者跟随一个星号(*)之后。同类型的多个标记应该组合在一起。例如,如果有三个@see标记,最好是一个挨着一个。
下面是一个类的文档注释的例子:
/** * This class draws a bar chart. * @author Herbert Schildt * @version 3.2 */
5.15.3 javadoc 的输出
javadoc程序将Java程序的源文件作为输入,输出几个包含该程序文档的HTML文件。每个类的信息都在其自己的HTML文件中。同时,javadoc还输出一个索引和一个层次结构树。
Javadoc还可生成其他HTML文件。不同版本的javadoc工作方式也有所不同,可以阅读Java开发系统的说明书了解所使用的版本的细节处理的规定。
看下面的例子,下面的例子说明了JavaDoc的使用方法:
范例:PersonJavaDoc.java
/** * Title: PersonJavaDoc<br> * <p> * Description: 通过 PersonJavaDoc 类来说明 Java 中的文档注释<br> * <p> * Copyright:(c) 2014 www.sun.com.cn<br> * <p> * Company : sun java </br> * * @author star984@163.com * @version 1.00 */ public class PersonJavaDoc { private String name = "Careers"; private String sex = "male"; private int age = 30; /** * 这是 PersonJavaDoc 对象无参数的构造方法 */ public PersonJavaDoc() { } /** * 这是 PersonJavaDoc 类的有参构造方法 * * @param name PersonJavaDoc 的名字 * @param sex PersonJavaDoc 的性别 * @param age PersonJavaDoc 的年龄 */ public PersonJavaDoc(String name, String sex, int age) { this.name = name; this.sex = sex; this.age = age; } /** * 这是设置 name 的值的方法,将参数 name 的值赋给变量 this.name * * @param name PersonJavaDoc 的名字 */ public void setName(String name) { this.name = name; } /** * 这是取得 name 的值的方法 * * @return 返回 name 的值 */ public String getName() { return name; } /** * 这是设置 age 的值的方法 * * @param age PersonJavaDoc 的年龄 */ public void setAge(int age) { if (age < 0) this.age = 0; else this.age = age; } /** * 这是取得 age 的值的方法 * * @return 返回 age 的值 */ public int getAge() { return age; } /** * 这是设置 sex 的值的方法,将参数 sex 的值赋给变量 this.sex * * @param sex PersonJavaDoc 的性别 */ public void setSex(String sex) { this.sex = sex; } /** * 这是取得 sex 的值的方法 * * @return 返回 sex 的值 */ public String getSex() { return sex; } /** * 这是显示输出的方法 */ public void shout() { System.out.println("我是 " + name + ",我性别 " + sex + ",今年 " + age + " 岁!"); } }
之后用javadoc命令进行编译,请看下面的命令:
javadoc -d PersonJavaDoc -version -author PersonJavaDoc.java
-d:表示生成目录,目录名称为 PersonJavaDoc
-version:表示要求 javadoc 程序在说明文件中加入版本信息。
-author:表示要求 javadoc 程序在说明文件中加入作者信息。
最终可以发现在硬盘上新生成了一个PersonJavaDoc的文件夹,如图5-15所示:
图5-15 PersonJavaDoc文件夹的内容
打开文件夹里的index.html文件可以看见如下所示的界面:
图5-16 index.html
·本章摘要:
1、“类”是把事物的数据与相关的功能封装在一起,形成的一种特殊结构,用以表达对真实世界的一种抽象概念。
2、Java把数据成员称为field(属性),把方法成员称为method(方法)。
3、由类所创建的对象称为instance,译为“实例”。
4、创建属于某类的对象,可通过下面两个步骤来达成:(1)、声明指向“由类所创建的对象”的变量。(2)、利用new创建新的对象,并指派给步骤一中所创建的变量。
5、要访问到对象里的某个属性(field)时,可通过“对象名称.属性”语法来实现,如果要调用封装在类里的方法,则可通过“对象名称.method”语法来实现。
6、有些方法不必传递任何数据给调用端程序,因此是没有返回值的。若方法本身没有返回值,则必须在方法定义语句前面加上关键字void。
7、私有成员(private member)可限定类中的属性,被限制成私有的属性仅能供同一类内的方法所访问。
8、类外部可访问到类内部的公有成员(public member)。
9、“封装”(encapsulation):是把属性和方法包装在一个类内以限定成员的访问,以起到保护数据的作用。
10、构造方法可视为一种特殊的方法,它的主要作用是为所创建的对象赋初值。
11、构造方法的名称必须与其所属的类的类名称相同,且不能有返回值。
12、从某一构造方法内调用另一构造方法,是通过this() 这个关键字来完成的。
13、构造方法有public与private之分。声明为public的构造方法可以在程序的任何地方被调用,所以新创建的对象都可以自动调用它。而被声明为private的则无法在该构造方法所在的类以外的其它地方被调用。
14、如果构造方法省略不写,Java则会自动调用默认的构造方法(默认的构造方法没有任何参数)。
15、“基本类型的变量”是指用int、double等关键字所声明的变量,而由类声明而得的变量,称之为“类类型的变量”,它是属于“非基本类型的变量”的一种。
16、对象也可以用数组来存放,但必须有下面两个步骤:(1)、声明类类型的数组变量,并用new分配内存空间给数组。(2)、用new产生新的对象,并分配内存空间给它。
17、如果在类Outer的内部再定义一个类Inner,此时类Inner称为内部类(inner class),而类Outer则称为外部类(outer class)。
感谢阅读。如果感觉此章对您有帮助,却又不想白瞟