Java修饰符------>static、native和final以及super和this关键字

时间:2022-01-19 00:39:58

修饰符
修饰符的作用是让被修饰的内容具备特定的功能,在程序中合理使用
修饰符可以在语法和功能上实现很多需要的效果。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 都是非静态的,所以这两个关键
字都无法在静态方法内部进行使用。