|
有时你希望定义一个类成员,使它的使用完全独立于该类的任何对象。通常情况下,类成员必须通过它的类的对象访问,但是可以创建这样一个成员,它能够被它自己使用,而不必引用特定的实例。在成员的声明前面加上关键字static(静态的)就能创建这样的成员。
如果一个成员被声明为static,它就能够在它的类的任何对象创建之前被访问,而不必引用任何对象。你可以将方法和变量都声明为static。static成员的最常见的例子是main( )。因为在程序开始执行时必须调用main(),所以它被声明为static。
声明为static的变量实质上就是全局变量。当声明一个对象时,并不产生static变量的拷贝,而是该类所有的实例变量共用同一个static变量。
声明为static的方法有以下几条限制:
· 它们仅能调用其他的static方法。
· 它们只能访问static数据。
· 它们不能以任何方式引用this或super(关键字super与继承有关,在下一章中描述)。
如果你需要通过计算来初始化你的static变量,你可以声明一个static块,static块仅在该类被加载时执行一次。 下面的例子显示的类有一个static方法, 一些static变量, 以及一个static初始化块:
// Demonstrate static variables,methods,and blocks.
class UseStatic {
static inta = 3;
static intb;
static voidmeth(int x) {
System.out.println("x = " + x);
System.out.println("a = " + a);
System.out.println("b = " + b);
}
static {
System.out.println("Static block initialized.");
b = a * 4;
}
publicstatic void main(String args[]) {
meth(42);
}
}
一旦UseStatic类被装载,所有的static语句被运行。首先,a被设置为3,接着static块执行(打印一条消息),最后,b被初始化为a*4或12。然后调用main(),main()调用meth(),把值42传递给x。3个println ( )语句引用两个static变量a和b,以及局部变量x 。
注意:在一个static方法中引用任何实例变量都是非法的。
下面是该程序的输出:
Static block initialized.
x = 42
a = 3
b = 12
在定义它们的类的外面,static方法和变量能独立于任何对象而被使用。这样,你只要在类的名字后面加点号运算符即可。例如,如果你希望从类外面调用一个static方法,你可以使用下面通用的格式:
classname.method( )
这里,classname 是类的名字,在该类中定义static方法。可以看到,这种格式与通过对象引用变量调用非static方法的格式类似。一个static变量可以以同样的格式来访问——类名加点号运算符。这就是Java如何实现全局功能和全局变量的一个控制版本。
下面是一个例子。在main()中,static方法callme()和static变量b在它们的类之外被访问。
class StaticDemo {
static inta = 42;
static intb = 99;
static voidcallme() {
System.out.println("a = " + a);
}
}
class StaticByName {
publicstatic void main(String args[]) {
StaticDemo.callme();
System.out.println("b = " +StaticDemo.b);
}
}
下面是该程序的输出:
a = 42
b = 99
|
一个变量可以声明为final, 这样做的目的是阻止它的内容被修改。这意味着在声明final变量的时候,你必须初始化它(在这种用法上,final类似于C/C++中的const)。例如:
final int FILE_NEW = 1;
final int FILE_OPEN = 2;
final int FILE_SAVE = 3;
final int FILE_SAVEAS = 4;
final int FILE_QUIT = 5;
你的程序的随后部分现在可以使用 FILE_OPEN等等,就好像它们是常数,不必担心它们的值会被改变。
为final变量的所有的字符选择大写是一个普遍的编码约定。声明为final的变量在实例中不占用内存。这样,一个final变量实质上是一个常数。
关键字final也可以被应用于方法,但是它的意思和它被用于变量实质上是不同的。final的第二种用法将在下一章描述继承时解释。
|
Enum作为Sun全新引进的一个关键字,看起来很象是特殊的class, 它也可以有自己的变量,可以定义自己的方法,可以实现一个或者多个接口。当我们在声明一个enum类型时,我们应该注意到enum类型有如下的一些特征。
1.它不能有public的构造函数,这样做可以保证客户代码没有办法新建一个enum的实例。
2.所有枚举值都是public , static , final的。注意这一点只是针对于枚举值,我们可以和在普通类里面定义变量一样定义其它任何类型的非枚举变量,这些变量可以用任何你想用的修饰符。
3.Enum默认实现了java.lang.Comparable接口。
4.Enum覆载了了toString方法,因此我们如果调用Color.Blue.toString()默认返回字符串”Blue”.
5.Enum提供了一个valueOf方法,这个方法和toString方法是相对应的。调用valueOf(“Blue”)将返回Color.Blue.因此我们在自己重写toString方法的时候就要注意到这一点,一把来说应该相对应地重写valueOf方法。
6.Enum还提供了values方法,这个方法使你能够方便的遍历所有的枚举值。
7.Enum还有一个oridinal的方法,这个方法返回枚举值在枚举类种的顺序,这个顺序根据枚举值声明的顺序而定,这里Color.Red.ordinal()返回0。
1.遍历所有有枚举值. 知道了有values方法,我们可以轻车熟路地用ForEach循环来遍历了枚举值了。
for (Color c: Color.values())
System.out.println(“find value:” + c);
2.在enum中定义方法和变量,比如我们可以为Color增加一个方法随机返回一个颜色。
public enum Color ...{
Red,
Green,
Blue;
/**//*
*定义一个变量表示枚举值的数目。
*(我有点奇怪为什么sun没有给enum直接提供一个size方法).
*/
private static int number = Color.values().length ;
/** *//**
* 随机返回一个枚举值
@return a random enum value.
*/
public static Color getRandomColor()...{
long random = System.currentTimeMillis() % number;
switch ((int) random)...{
case 0:
return Color.Red;
case 1:
return Color.Green;
case 2:
return Color.Blue;
default : return Color.Red;
}
}
}
可以看出这在枚举类型里定义变量和方法和在普通类里面定义方法和变量没有什么区别。唯一要注意的只是变量和方法定义必须放在所有枚举值定义的后面,否则编译器会给出一个错误。
3.覆载(Override)toString, valueOf方法。
前面我们已经知道enum提供了toString,valueOf等方法,很多时候我们都需要覆载默认的toString方法,那么对于enum我们怎么做呢。其实这和覆载一个普通class的toString方法没有什么区别。
….
public String toString()...{
switch (this)...{
case Red:
return "Color.Red";
case Green:
return "Color.Green";
case Blue:
return "Color.Blue";
default:
return "Unknow Color";
}
}….
这时我们可以看到,此时再用前面的遍历代码打印出来的是
Color.Red
Color.Green
Color.Blue
而不是
Red
Green
Blue.
可以看到toString确实是被覆载了。一般来说在覆载toString的时候我们同时也应该覆载valueOf方法,以保持它们相互的一致性。
4.使用构造函数。
虽然enum不可以有public的构造函数,但是我们还是可以定义private的构造函数,在enum内部使用。还是用Color这个例子。
public enum Color ...{
Red("This is Red"),
Green("This is Green"),
Blue("This is Blue");
private String desc;
Color(String desc)...{
this.desc = desc;
}
public String getDesc()...{
return this.desc;
}
}
这里我们为每一个颜色提供了一个说明信息
public enum Color ...{
Red ...{
public String toString()...{
return "Color.Red";
}
},
Green ...{
public String toString()...{
return "Color.Green";
}
},
Blue...{
public String toString()...{
return "Color.Blue";
}
};
}
, 然后定义了一个构造函数接受这个说明信息。
要注意这里构造函数不能为public或者protected, 从而保证构造函数只能在内部使用,客户代码不能new一个枚举值的实例出来。这也是完全符合情理的,因为我们知道枚举值是publicstatic final的常量而已。
5.实现特定的接口
我们已经知道enum可以定义变量和方法,它要实现一个接口也和普通class实现一个接口一样,这里就不作示例了。
6.定义枚举值自己的方法。
前面我们看到可以为enum定义一些方法,其实我们甚至可以为每一个枚举值定义方法。这样,我们前面覆载 toString的例子可以被改写成这样。
从逻辑上来说这样比原先提供一个“全局“的toString方法要清晰一些。
总的来说,enum作为一个全新定义的类型,是希望能够帮助程序员写出的代码更加简单易懂,个人觉得一般也不需要过多的使用enum的一些高级特性,否则就和简单易懂的初衷想违背了。
|
从jdk5.0开始,import不仅可以导入类,还可以导入静态方法和静态域。
如果在源文件顶部添加import static java.lang.System.*;
那么就可以使用System类的静态方法和静态域,而不必加类前缀如:
out.println("hello world"); 相当于 System.out.println("helloworld");
exit(0); 相当于 System.exit(0);
静态方法导入和导入静态域有两个实际的应用:
1.算术函数,对Math类使用静态导入,就能更自然的使用算术函数
sqrt(pow(x,2)+pow(y,2));
2.笨重的常量,如果需要使用大量带有冗长名字的常量,就应该使用静态导入
Date d = new Date();
if(d.get(DAY_OF_WEEK) == MONDAY)
看起来比
if(d.get(Calendar.DAY_OF_WEEK) == Calendar.MONDAY)
清晰
代码:
import static java.lang.Math.pow;
import static java.lang.Math.sqrt;
import static java.lang.System.out;
import static java.util.Calendar.MONDAY;
import java.util.Date;
public class StaticImportTest {
/**
* @param args
*/
public static void main(String[] args) {
out.println("hello");
int x = 5,y=10;
double d = sqrt(pow(x,2) + pow(y,2));
out.println(d);
Date date = new Date();
if(date.getDay() == MONDAY)
out.println("今天星期1");
else{
out.println("今天星期"+ date.getDay());
}
}
}
|
Java语言中答应有一种叫做抽象方法的东西,他只是一个名字没有具体的实现。像是这样: public abstract void abc();使用了abstract要害字,结尾用“;”结束。与前几节我们用的方法都是具体方法,是有实现的。哪怕方法体中什么也不写public void abc() { } 也是具体方法。
概念:包含一个或多个抽象方法的类称为抽象类。抽象类也必须声明abstract要害字。抽象类的使用有着一些限制,不能创建抽象类的实例。假如子类实现了抽象方法,则可以创建该子类的实例对象。要是子类也不实现的话,这个子类也是抽象类,也不能创建实例。
接口是什么东西呢?接口是比抽象类更抽象的类。举例: public interface Name { }接口里面的方法全都是抽象的,里面的变量全都是final的常量,而且实现接口的类必须将所有的抽象方法全部实现。抽象类里也可以有具体的方法。所以说,接口是最抽象的,其次是抽象类,而具体类本身就是对现实世界的抽象。软件开发本身就是将现实世界抽象成计算机世界。
因为抽象类和接口比具体类抽象,所以使用时他们总是被继续而被实现的。不过继续他们的类不只是一个,有很多类实现他们的抽象方法。一个方法有多种实现方式,这里用到了OOP中的多态性。这使得设计变得非常清楚。因为基类是抽象类或是接口做一个描述,底下继续的类有若干个,我们只需要对接口或抽象类操作,也用不着管有多少个实现。假如是多人共同开发的项目的话,是非常有意义的。你自己写个东西,怎么实现的也不用告诉别人,别人看个接口就够了。
接口的实现用要害字implement 而不是extends.假如用了extends的那就是继续这个接口。那么那个子类也是接口,是原来的子接口。举个接口的例子吧:
实践:
//声明一个接口 public interface Say { public void sayMessage(); } //两个实现类 public class SayHello implements Say { public void sayMessage() { System.out.println("hello"); }} public class SayHi implements Say { public void sayMessage() { System.out.println("Hi"); }} //这是一个测试类 public class TestSay { public static void main(String[] args) { //同样都是用Say这个接口类型实例,却可以输出两个结果 Say say = new SayHello(); say.sayMessage(); Say say1 = new SayHi(); say1.sayMessage(); }} |
接口还有一个重要的作用,我们在面向对象那节课里提过一个概念,java语言中只有单继续,就是说只能从一个父类继续。单继续的好处是,一旦继续的太多了,改了一个类子类就都变了。牵一发,而动全身。那么假如我们想继续多个父类的特性怎么办呢?就用接口吧,这个类可以先继续一个类,再去实现其它的接口,接口里面都是抽象方法,不会造成,牵一发,而动全身的效应。改变多继续的特性,也是对C++语言的一项改进。
业界有一种说法说,与其说java是面向对象编程,还不如说它是面向接口编程。强调的方面是接口的抽象描述性。它也是对C++的一种改进,C++里面没有接口。所以说java语言适合多人团队合作的大项目,看一个接口就可以了,后面怎么实现的可以不管。
|
1. 现实世界中,公共物品是不是放在大家都能看到或找到的地方;与我们学的关键字static有什么相似和区别?
2. 生活中有些事情是固定不变的,比如法律制度是很多年不变的;这些和我们学的关键字final有什么相似和区别的地方?
3. 如果要建一座大楼,首先要设计图纸;然后把主题的框架搭建好;然后再具体的做进一步的细致工作;最后,一座大楼就建成了;这些步骤与我们软件工程之前存在什么关系;哪个步骤类似于软件中的设计抽象类和接口的过程?
|
在本章中,我们主要学习了:
u 关键字static、final及相关应用;
u Java枚举类型;
u Java静态导入
u 抽象类和接口的使用
|
英文 全文 中文
Static Static 静态的
Final Final 终结
Enum Enum 枚举
Import Import 导入
|
总结人类使用火的手段及火的器具;比如灭火器(经常固定位置的)、打火机(经常使用的)、原始人使用的火种(经典神圣的)等;可以对人对火的形容词汇做个总结归结到枚举类型中;例如:风风火火、火急火燎等;并对人类的进化做继承归纳;用面向对象及本章缩写的知识写出相关代码;