Java的修饰符

Posted xyqing525

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java的修饰符相关的知识,希望对你有一定的参考价值。

转自:http://blog.csdn.net/manyizilin/article/details/51926230#L42

修饰符:

像其他语言一样,Java可以使用修饰符来修饰类中方法和属性。主要有两类修饰符:

访问修饰符:default, public , protected, private

非访问修饰符:final, abstract, strictfp

修饰符一般使用在一个语句的前端,例:

  1. public void Pig{  
  2.    int a = 1;  
  3.    protected String b = "b";   
  4.    private static final int c = 1;  
  5.      
  6.  }  


访问修饰符
Java中,可以使用访问控制符来保护对类、变量、方法和构造方法的访问。Java支持4种不同的访问权限。

默认的,也称为 default,在同一包内可见,不使用任何修饰符。

私有的,以 private 修饰符指定,在同一类内可见。

共有的,以 public 修饰符指定,对所有类可见。

受保护的,以 protected 修饰符指定,对同一包内的类和所有子类可见。

  Java中类的修饰符有以下几种:private 、default(package)、protect、public,其范围如下表:

范围 private default protected public
同一类
同一包中的类  
同一包中的类、不同包中的子类    
所有      

默认访问修饰符-不使用任何关键字

使用默认访问修饰符声明的变量和方法,对同一个包内的类是可见的。接口里的变量都隐式声明为public static final,而接口里的方法默认情况下访问权限为public。

私有访问修饰符-private

私有访问修饰符是最严格的访问级别,所以被声明为private的方法、变量和构造方法只能被所属类访问,并且类和接口不能声明为private。

  1. public class Pig{  
  2.   
  3.    private int num = 1;  
  4.    public int getNum(){  
  5.     return num;  
  6.    }  
  7.    public void setNum(int num){  
  8.     this.num = num;  
  9.    }  
  10. }  
  11.    

公有访问修饰符-public

被声明为public的类、方法、构造方法和接口能够被任何其他类访问。

如果几个相互访问的public类分布在不同的包中,则需要导入相应public类所在的包。由于类的继承性,类所有的公有方法和变量都能被其子类继承。


受保护的访问修饰符-protected

被声明为protected的变量、方法和构造器能被同一个包中的任何其他类访问,也能够被不同包中的子类访问。

Protected访问修饰符不能修饰类和接口,方法和成员变量能够声明为protected,但是接口的成员变量和成员方法不能声明为protected。

子类能访问Protected修饰符声明的方法和变量,这样就能保护不相关的类使用这些方法和变量。

访问控制和继承

请注意以下方法继承的规则:

  • 父类中声明为public的方法在子类中也必须为public。

  • 父类中声明为protected的方法在子类中要么声明为protected,要么声明为public。不能声明为private。

  • 父类中声明为private的方法,不能够被继承。

非访问修饰符

为了实现一些其他的功能,Java也提供了许多非访问修饰符。

static修饰符,用来创建类方法和类变量。

Final修饰符,用来修饰类、方法和变量,final修饰的类不能够被继承,修饰的方法不能被继承类重新定义,修饰的变量为常量,是不可修改的

Abstract修饰符,用来创建抽象类和抽象方法

Synchronized和volatile修饰符,主要用于线程的编程。

Static修饰符

         静态变量:

  • Static关键字用来声明独立于对象的静态变量,无论一个类实例化多少对象,它的静态变量只有一份拷贝。 静态变量也被成为类变量。局部变量不能被声明为static变量。

         静态方法:

  • Static关键字用来声明独立于对象的静态方法。静态方法不能使用类的非静态变量。静态方法从参数列表得到数据,然后计算这些数据。(这就是访问权限的问题了)。

         被static 修饰符修饰的成员方法和成员变量是独立于该类的任何对象的,可以被所有的类所共享。应为在加载(还没有实例化该类之前),JVM就已经为静态方法和静态变 量分配内存空间。到时候只要使用类名去访问就可以了。被public 修饰的static成员变量和成员方法本质是全局变量或者是全局方法。任何一个类都可以同个类名访问内部和成员变量和成员方法

     static代码块

      也叫做静态代码块,独立于类成员的代码块。在一个类中可以有多个静态代码块,位置可以任意,它不再任何方法体内,JVM加载类的时候,会自动执行静态代码块(记住:在加载类的时候就已经执行了,不用等待实例化类)。如果是由对各静态代码块,那么JVM就会按照这些静态代码块出现的顺序执行它们。每个静态代码块自会执行一次。(就是这个程序从开始运行到结束只会执行一次)。

  1. public class HellowJava {  
  2.   
  3. static{}  
  4.  public static void main(String[] arguments) {  
  5.         System.out.println("main程序");  
  6.         new StaticTest();  
  7.           
  8.     }  
  9.   
  10. }  
  11.   
  12. class StaticTest{  
  13.       
  14.     public StaticTest(){  
  15.         System.out.println("StaticTest");  
  16.     }  
  17.     static{  
  18.         System.out.println("静态代码块0");  
  19.     }  
  20.     static{  
  21.         System.out.println("静态代码块1");  
  22.     }  
  23.       
  24. }  


执行结果:

main静态代码块
main程序
静态代码块0
静态代码块1
StaticTest


解释:程序执行后,先加载 HellowJava 类 ,这时候执行这个类中的静态代码块,加载完毕那个后直接执行 mian() 方法,

  按顺序先执行 System.out.println("main程序");

  再加载StaticTest 类,这时候执行StaticTest中的静态代码块,并且按顺序执行这些代码块

 最后实例化这个StaticTest类(这时候就会调用构造方法)


  1. public class HellowJava {  
  2.   
  3.     static{  
  4.         System.out.println("main静态代码块");  
  5.     }  
  6.     public static void main(String[] arguments) {  
  7.         System.out.println("main程序"<span style="color:#FF0000;">+StaticTest.a)</span>;  //这里的就造成了 StaticTest 类的加载(就是静态代码块会执行)<pre name="code" class="html">StaticTest  

new StaticTest(); }}
class StaticTest{
public StaticTest(){
System.out.println("StaticTest"); }
static{ a=1; System.out.println("静态代码块0"); }
static{ System.out.println("静态代码块1"); }
public static final int a ;
} 结果:

main静态代码块静态代码块0静态代码块1main程序1   StaticTest解释:本来以为static 修饰的代码都是会按顺序直接执行或者分配内存的。看来好像不是这样子。怎么说呢。从最后一行代码可以看到我把静态变量的定义放在了最后面,但是并没有出现 任何报错,说明了JVM记载的时候是首先加载静态变量,再加载静态代码块的。(个人理解,喜欢就喷吧)

static和final一块用表示什么

static final用来修饰成员变量和成员方法,可以理解为“全局常量”。

   对于变量 :表示一旦给定值就不可以更改,并且可以通过类名来访问。

  对于方法: 表示不可以覆盖,并且可以通过类名来访问。

声明静态方法的限制:

 仅能调用其他的static方法;

仅能访问static 数据

不能以任何形式引用this 和super(和继承有关)

Final

Final方法

类中的Final方法可以被子类继承,但是不能被子类修改。

声明final方法的主要目的是防止该方法的内容被修改。

Final类

Final类不能被继承,没有类能够继承final类的任何特性。

Final修饰符

final变量能被显性的初始化,并且只可以初始化一次。被声明为final的对象不能指向不同的对象。但是final中的值是可以改变的。也就是说final中对象的引用不能够改变,但是里边的值可以改变。

例如: final Integer a =10;  Integer b =20;  可以这样改变  a =11;  或 a =20;  但是不可以  a =b ;

Final修饰符通常和static修饰符一起使用来创建类常量。

final修饰的成员变量
(1)final修饰的成员变量一旦赋值后,不能被重新赋值。
(2)final修饰的实例Field,要么在定义该Field的时候指定初始值,要么在普通初始化块或构造器中指定初始值。但是如果在普通初始化块中为某个实例Field指定了初始值,则不能再在构造器中指定初始值。
(3)final修饰的类Field,要么在定义该Field的时候指定初始值,要么在静态代码块中定义初始值。
(4)如果在构造器或初始化块中对final成员变量进行初始化,则不要在初始化之前就访问该成员的值。

 

  1. class finalTest{  
  2.       
  3.     final int a = 1;  //直接赋值  
  4.     final String b ;  
  5.     {  
  6.         b = "b";   //在代码块中赋值  
  7.         //这个代码块块只有当实例化这个类的时候优先于静态方法执行,  
  8.         //和构造方法无关,实例化的时候都会去执行 //比构造函数先执行  
  9.     }  
  10.       
  11.     final boolean c;  
  12.     public finalTest(){   //在构造函数中赋值  
  13.         c = false;  
  14.     }  
  15.       
  16.     final static int d = 8;        //直接赋值   
  17.       
  18.     final static int e;  
  19.     static{  
  20.         e = 1;     //在静态代码块中赋值  
  21.     }  
  22.       
  23. }  
final 的局部变量
(1)系统不会对局部变量进行初始化。布局变量必须要显示初始化。所以final修饰的局部变量既可以在定义的时候指定默认值,也可以不指定默认值。
(2)final修饰形参的时候,不能赋值

final修饰基本数据类型变量和修饰引用类型变量的区别
final修饰基本数据类型的变量,一旦该变量赋值后,就不能被重新复制赋值了。
对于引用类型的变量,保存的只是引用,final修饰的应用类型的变量只是保证这个对象不改变,但是这个对象的内部内容是可以发生改变的。比如:
 
  1. final  List<Stringlist = new ArrayList();  
  2. list.add("a"); list.add("b");  
这里可以看到list在被使用final修饰后还是可以往里边添加内容的,list的内部可以改变。
再看:
  1. final int a = 1;  
  2. a =2;  
这个系统会报错。

final 的宏变量

(1)final 的一个重要的用途就是宏变量,当定义final变量是就制定了初值,这个初值是在编译的时候就加载进来了。编译会把程序中所用到的该变量的地方替换成该变量的值。

public class FinalTest {
    public static void main(String[] args){
        final String name = "小明" + 22.0;
        final String name1 = "小明" + String.valueOf(22.0);
        System.out.println(name == "小明22.0");
        System.out.println(name1 == "小明22.0");
    }
}

结果:

true
false


final String name1 = "小明"+String.valueOf(22.0); 中调用了String类的方法,所以在编译的时候无法确定name1的值,所以name1不会当作是宏变量。


package cn.lsl;

public class FinalTest {
    public static void main(String[] args){
        String s1 = "小明";
        String s2 = "小" + "明";
        System.out.println(s1 == s2);    //true
        
        String str1 = "小";
        String str2 = "明";
        String s3 = str1 + str2;
        System.out.println(s1 == s3);        //false
        
        //宏替换
        final String str3 = "小";
        final String str4 = "明";
        String s4 = str3 + str4;
        System.out.println(s1 == s4);        //true
    }
}

 

(1)Java会使用常量池管理直接使用过的字符串直接量。 String  a = "Hellow" ; 那么字符串池会缓存一个字符串 “Hellow”,当执行String b = "Hellow",会直接让b 指向“Hellow”  这个字符串, 所以a == b ;是true

(2)String s3 = str1+str2;在编译时没有办法确定s3的值。

(3)String s4 = str3+str4;应为执行了宏变换,所以在编译的时候就已经确定了s4的值


用final修饰的方法不能被重写。用final修饰的类不能有子类。

不可变类
不可变类是指创建该类的实例后,该实例的Field是不可改变的。
如果创建自定义的不可变类,应该遵循如下规则
(1)使用private和final修饰符来修饰该类的Field。
(2)提供带参数的构造器,用于传入参数来初始化类里的Field。
(3)仅为该类的Field提供getter方法,不要为该类的Field提供setter方法。
(4)如果有必要,重写Object类的hashCode和equals方法。


Abstract修饰符

抽象类:

abstract修饰符不能用来实例化对象,声明这个抽象类的唯一目的是为了将来对这个类进行扩充。

一个类不能同时被abstract和final修饰,如果这个类包含抽象方法,那么这个类一定要声明为抽象类,否则出现错误。

抽象类的内部可以包含抽象方法和非抽象方法。

抽象类如下:

  1. abstract class CaraVan{  
  2.    private double price;  
  3.    private  String model ;  
  4.    private String year;  
  5.    public abstract void setName(String name);  //抽象方法  
  6.    public abstract String getName();  
  7.  }  


抽象方法:

抽象方法是一种没有任何实现的方法,具体的实现依靠子类方法实现。该方法也不可以声明为final 和static

任何继承了抽象类的子类必须实现父类的中抽象方法,除非这个子类也是抽象类。

如果一个类中包含抽象方法,那么这个类必须是抽象类,一个抽象类中也可以不包含任何的抽象方法。

例:

  1. abstract class Animal{  
  2.     public abstract void run(); //抽象方法。  
  3.       
  4. }  
  5.   
  6.  class Pig extends Animal{  
  7.   
  8.     @Override  
  9.     public void run() {  
  10.         // TODO Auto-generated method stub  
  11.         //do ...  
  12.     }  
  13.       
  14. }  


和接口(Interface)的区别。

1、相同点:

(1)都是抽象类,都是不能实例化。

(2)interface实现类及abstact class 的之类都必须实现已经声明的抽象方法。

2、不同点

(1)interface 的实现使用的是implements ,而abstract class 的实现使用的是extends.

(2)一个类可以实现多个interface但是只可以继承一个abstract class

(3)interface强调功能的实现,而abstract 强调所属的关系。

(4)实现显示不一样,interface的每个方法都是抽象方法,没有方法体,而abstract class 子类可以选择的实现这些方法(其他的可以在abstract class类中直接实现方法体)。


抽象类的这个选择有两点含义:

      一是Abastract class中并非所有的方法都是抽象的,只有那些冠有abstract的方法才是抽象的,子类必须实现。那些没有abstract的方法,在Abstrct class中必须定义方法体。

      二是abstract class的子类在继承它时,对非抽象方法既可以直接继承,也可以覆盖;而对抽象方法,可以选择实现,也可以通过再次声明其方法为抽象的方式,无需实现,留给其子类来实现,但此类必须也声明为抽象类。既是抽象类,当然也不能实例化。

(5)interface是完全抽象的。只能声明方法,权限也只能声明为public,不能定义方法体,也不可以实例化变量(都是 public static fianl 类型)

详细参考:http://blog.csdn.net/zhandoushi1982/article/details/8458081


Synchronized修饰符

Synchronized关键字声明的方法同一时间只能被一个线程访问。Synchronized修饰符可以应用于四个访问修饰符。

实例:

  1. public synchronized void setName(){  
  2.         //...  
  3. }  

参考详情:

  http://www.cnblogs.com/GnagWang/archive/2011/02/27/1966606.html

  http://blog.csdn.net/luoweifu/article/details/46613015

测试代码: