修饰符
修饰符的作用是让被修饰的内容具备特定的功能,在程序中合理使用
修饰符可以在语法和功能上实现很多需要的效果。Java 语言中的修饰符主要有 5
个:static、final、native、abstract 和 synchronized。这里首先讲解 static、
final 和 native 的作用。
static 修饰符
static 关键字的中文意思是静态的,该修饰符可以修饰成员变量,成
员常量和成员方法。使用该关键字修饰的内容,在面向对象中 static 修饰的内
容是隶属于类,而不是直接隶属于对象的,所以 static 修饰的成员变量一般称
作类变量,而 static 修饰的方法一般称作类方法。另外,static 还可以修饰代
码块,下面进行详细的介绍。
静态变量
static 修饰的变量称作静态变量。静态变量和一般的成员变量不同,
一个类在加载到内存时,静态变量只初始化一次,也就是说所有对象的静态变量
在内存中都只有一个存储位置,每个对象中的静态变量都指向内存中同一个地
址,它是在所有的对象之间共享的数据。另外静态变量在引用时比较方便。所以
一般在需要实现以下两个功能时使用静态变量:
l 在对象之间共享值时
l 方便访问变量时
下面首先说一下非静态变量(没有 static 修饰符修饰的成员变量)在内存中如何
存储的。示例代码如下:
//文件名 Box.java
public class Box{
int length;
int width;
int height;
public Box(int l,int w,int h){
length = l;
width = w;
height = h;
}
}
//文件名 TestBox.java
public class TestBox{
public static void main(String[] args){
Box a = new Box(10,20,30);
Box b = new Box(40,20,10);
}
}
则对象 a 和对象 b 在内存中的存储格式如下图所示:
对象 a 对象 b
从上面的图可以看出,非静态变量的值在每个对象中都有独立的存储空间,不同
对象间这些值之间没有管理,也就是说每个对象都为内部的每个非静态的变量分
配独立的存储空间,所以每个对象中非静态变量是隶属于对象,也就是说在每个
对象中可能是不同的。
简单介绍了非静态变量在对象中的存储以后,下面再来看一下静态变量是如何进
行存储的。示例代码如下:
//文件名 StaticVar.java
public class StaticDemo{
static int m;
int n;
char c;
}
//文件名 TestStaticVar.java
public class TestStaticVar{
public static void main(String[] args){
StaticVar sv1 = new StaticVar();
StaticVar sv2 = new StaticVar();
}
}
则对象 sv1 和对象 sv2 在内存中存储的格式如下图所示:
对象 sv1 对象 sv2
对于 StaticDemo 类型的对象 sv1 和 sv2 来说,由于使用默认的构造方法进行构
造,所以每个成员变量都被初始化为对应数据类型的默认值,int 的默认值为 0,
char 的默认值为编号为 0 的字符,所以 sv1 和 sv2 对象中存储的值如上图所示。
而静态变量的存储和非静态变量的存储不同,在 Java 虚拟机内部,第一次使用
类时初始化该类中的所有静态变量,以后就不再进行初始化,而且无论创建多少
个该类的对象,静态变量的存储在内存中都是独立于对象的,也就是 Java 虚拟
机单独为静态变量分配存储空间,所以导致所有的对象内部的静态变量在内存中
存储时只有一个空间。这样就导致使用任何一个对象对该值的修改都是使该存储
空间中的值发生改变,而其它对象在后续引用时就跟着发生了变化。静态变量就
是使用这样的方式在所有的对象之间进行数值共享的。
静态变量在实际使用时,可以通过只存储一次来节约存储空间,这个特性导致在
类内部定义的成员常量一般都做成静态的,因为常量的值在每个对象中都是相同
的,而且使用 static 修饰也便于对成员常量的引用。
在类外部访问某类中静态变量(常量)的语法格式为:
类名.成员变量(常量)
例如:
StaticDemo.m
这样方便对于成员变量的访问。当然,语法上也不禁止使用:对象.成员变量,
这样的语法格式进行访问,但是一般不推荐这样使用,而且有些类是无法创建对
象的。
注意:static 关键字不能修饰成员方法或构造方法内部的变量。
静态方法
static 修饰的方法称作静态方法。静态方法和一般的成员方法相比,
不同的地方有两个:一是调用起来比较方便,二是静态方法内部只能使用静态的
成员变量。所以一般静态方法都是类内部的独立的功能方法。例如为了方便方法
的调用,Java API中的Math类中所有的方法都是静态的,而一般类内部的static
方法也是方便其它类对该方法的调用。
示例代码如下:
//文件名 MyMath.java
public class MyMath{
public static int max(int a,int b){
return (a > b ? a : b);
}
}
//文件名 TestMyMath.java
public class TestMyMath{
public static void main(String[] args){
int m = 10;
int n = 20;
int k = MyMath.max(m,n);
}
}
静态方法在类的外部进行调用时不需要创建对象,使用类名.方法名
(参数)这样的语法格式进行调研,简化了代码的编写。
使用静态方法时,需要特别注意的是静态方法内部使用该类的非静态
成员变量,否则将出现语法错误。
静态方法是类内部的一类特殊方法,只有在需要时才将对应的方法声
明成静态的,一个类内部的方法一般都是非静态的。
静态代码块
静态代码块指位于类声明的内部,方法和构造方法的外部,使用static
修饰的代码块。静态代码块在该类第一次被使用时执行一次,以后再也不执行。
在实际的代码中,如果需要对类进行初始化的代码,可以写在静态代码块的内部。
示例代码如下:
//文件名 StaticBlock.java
public class StaticBlock{
static{
System.out.println(“静态代码块!”);
}
}
静态代码块是一种特殊的语法,熟悉该语法的特点,在实际程序中根
据需要使用。
final 修饰符
final 关键字是最终的、最后的意思,在程序中可以用来修饰类、成
员变量和方法的声明,由该关键字修饰的内容都是不可变的。
final 数据
final 修饰的数据是常量,常量既可以出现在类的内部,也可以出现
在方法或构造方法的内部。在程序中常量只能赋值一次。
其它说明可以参看前面的常量介绍。
在程序中,一般类内部的成员常量为了方便调用,一般都使用 static
修饰符进行修饰。示例代码如下:
/**
* 常量使用
*/
public class Student {
/**性别*/
int sex;
/**男性*/
public final static int MALE = 0;
/**女性*/
public final static int FEMALE = 1;
}
final 方法
final 关键字也可以修饰方法,final 修饰的方法称作最终方法,最终
方法不能被覆盖,也就是不能在子类的内部重写该方法。
使用 final 修饰方法,可以在一定程度上提高该方法的执行速度,应
为在调用该方法时,就不需要进行覆盖的判断了。
final 类
final 关键字也可以修饰类,final 修饰的类称作最终类,最终类不能
被继承,也就是该类不能有子类。
final 类内部的每个方法都是 final 方法。
native
native 关键字是“本地的”意思,native 修饰的方法,只有方法的声
明使用 java 语言实现,而方法内部的代码都是在 Java 虚拟机内部使用其它语言
实现。
一般 native 的方法,都是和系统操作有关的方法,或者是基于底层实
现效率比较高的方法,常见于系统类中。例如 System 类的 arraycopy 方法等。
this 和 super
下面再来介绍一下 this 和 super 关键字的使用。在程序中通过使用
this 和 super 关键字,可以实现对于类内部很多内容方便的引用,也有助于理
解面向对象的实现原理,更方便的理解面向对象技术的内部实现。
this 关键字
this 关键字代表自身,在程序中主要的使用用途有以下几个方面:
l 使用 this 关键字引用成员变量
l 使用 this 关键字在自身构造方法内部引用其它构造方法
l 使用 this 关键字代表自身类的对象
l 使用 this 关键字引用成员方法
引用成员变量
在一个类的方法或构造方法内部,可以使用“this.成员变量名”这样
的格式来引用成员变量名,有些时候可以省略,有些时候不能省略。首先看一下
下面的代码:
/**
* 使用 this 引用成员变量
*/
public class ReferenceVariable {
private int a;
public ReferenceVariable(int a){
this.a = a;
}
public int getA(){
return a;
}
public void setA(int a){
this.a = a;
}
}
在该代码的构造方法和 setA 方法内部,都是用 this.a 引用类的成员
变量。因为无论在构造方法还是 setA 方法内部,都包含 2 个变量名为 a 的变量,
一个是参数 a,另外一个是成员变量 a。按照 Java 语言的变量作用范围规定,参
数 a 的作用范围为构造方法或方法内部,成员变量 a 的作用范围是类的内部,这
样在构造方法和 setA 方法内部就存在了变量 a 的冲突,Java 语言规定当变量作
用范围重叠时,作用域小的变量覆盖作用域大的变量。所以在构造方法和 setA
方法内部,参数 a 起作用。
这样需要访问成员变量 a 则必须使用 this 进行引用。当然,如果变量
名不发生重叠,则 this 可以省略。
但是为了增强代码的可读性,一般将参数的名称和成员变量的名称保
持一致,所以 this 的使用频率在规范的代码内部应该很多。
引用构造方法
在一个类的构造方法内部,也可以使用 this 关键字引用其它的构造方
法,这样可以降低代码的重复,也可以使所有的构造方法保持统一,这样方便以
后的代码修改和维护,也方便代码的阅读。
下面是一个简单的示例:
/**
* 使用 this 关键字引用构造方法
*/
public class ReferenceConstructor {
int a;
public ReferenceConstructor(){
this(0);
}
public ReferenceConstructor(int a){
this.a = a;
}
}
这里在不带参数的构造方法内部,使用 this 调用了另外一个构造方
法,其中 0 是根据需要传递的参数的值,当一个类内部的构造方法比较多时,可
以只书写一个构造方法的内部功能代码,然后其它的构造方法都通过调用该构造
方法实现,这样既保证了所有的构造是统一的,也降低了代码的重复。
在实际使用时,需要注意的是,在构造方法内部使用 this 关键字调用
其它的构造方法时,调用的代码只能出现在构造方法内部的第一行可执行代码。
这样,在构造方法内部使用 this 关键字调用构造方法最多会出现一次。
代表自身对象
在一个类的内部,也可以使用 this代表自身类的对象,或者换句话说,
每个类内部都有一个隐含的成员变量,该成员变量的类型是该类的类型,该成员
变量的名称是 this,实际使用 this 代表自身类的对象的示例代码如下:
/**
* 使用 this 代表自身类的对象
*/
public class ReferenceObject {
ReferenceObject instance;
public ReferenceObject(){
instance = this;
}
public void test(){
System.out.println(this);
}
}
在构造方法内部,将对象 this 的值赋值给 instance,在 test 方法内
部,输出对象 this 的内容,这里的 this 都代表自身类型的对象。
引用成员方法
在一个类的内部,成员方法之间的互相调用时也可以使用“this.方法
名(参数)”来进行引用,只是所有这样的引用中 this 都可以省略,所以这里就
不详细介绍了。
super 关键字
super 关键字的中文意思是超级的,使用 super 关键字可以在子类中
引用父类中的内容。主要的使用形式有以下几种:
l 在子类的构造方法内部引用父类的构造方法
l 在子类中调用父类中的成员方法
l 在子类中调用父类中的成员变量
引用父类构造方法
在构造子类对象时,必须调用父类的构造方法。而为了方便代码的编
写,在子类的构造方法内部会自动调用父类中默认的构造方法。但是如果父类中
没有默认的构造方法时,则必须手动进行调用。
使用 super 可以在子类的构造方法内部调用父类的构造方法。可以在子类的构造
方法内部根据需要调用父类中的构造方法。
使用 super 关键字调用父类构造方法的示例代码如下:
//文件名:SuperClass.java
public class SuperClass {
public SuperClass(){}
public SuperClass(int a){}
}
//文件名:SubClass.java
public class SubClass extends SuperClass {
public SubClass(){
super(); //可省略
}
public SubClass(int a){
super(a);
}
public SubClass(String s){
super(); //可省略
}
}
在该示例代码中,SubClass 继承 SuperClass 类,在 SubClass 类的构造方法内
部可以使用 super 关键字调用父类 SubClass 的构造方法,具体调用哪个构造方
法没有限制,可以在子类内部根据需要进行调用,只是根据调用的构造方法不同
传入适当的参数即可。
由于 SubClass 类的父类 SuperClass 内部有默认的构造方法,所以 SubClass 的
构造方法内部 super()的代码可以省略。
和使用 this 关键字调用构造方法一样,super 调用构造方法的代码只能出现在
子类构造方法中的第一行可执行代码。这样 super 调用构造方法的代码在子类的
构造方法内部则最多出现一句,且不能和 this 调用构造方法的代码一起使用。
引用父类成员方法
在子类中继承了父类中的成员方法,一般可以直接通过方法名使用,
但是如果在子类中覆盖了父类的成员方法以后,如果需要在子类内部调用父类中
被覆盖的成员方法时则不能直接调用了,这样就又需要使用 super 关键字了。
示例代码如下:
//文件名:SuperClass2.java
public class SuperClass2 {
public void test(){}
public void print(int a){
System.out.println("SuperClass2: " + a);
}
}
//文件名:SubClass2
public class SubClass2 extends SuperClass2 {
public void print(int a){
super.print(a);
System.out.println("SubClass2");
}
public void t(){
super.test(); //super 可省略
super.print(0); //不可省略
}
}
引用父类成员变量
在子类中如果引用父类的成员变量,也可以使用“super.成员变量”
来引用,只是一般成员变量的覆盖是没有意义的,所以这个时候都可以直接使用
成员变量名进行引用,所以这里的 super 都可以省略。
注意的问题
最后,在实际使用 this 和 super 时,除了上面介绍到的需要注意的问
题以外,还需要特别注意的是,this 和 super 都是非静态的,所以这两个关键
字都无法在静态方法内部进行使用。