1、Java 修饰符的使用武汉理工大学计算机科学与技术学院软件工程 1004班 陈文伟理论部分java 中的类和方法的修饰符 Java 程序在定义类时,除了使用class 关键字标识之外,还可以在 class 之前增加若干类的修饰符来修饰限定所定义的类的特性。类的修饰符分为访问控制符和非访问控制符两大类。修饰符之间的先后排列次序对类的性质没有任何影响。一,非访问修饰符。abstract 修饰符Abstract 修饰符表示所修饰的类没有完全实现,还不能实例化。如果在类的方法声明中使用 abstract 修饰符,表明该方法是一个抽象方法,它需要在子类实现。如果一个类包含抽象函数,则这个类也是抽象类,必
2、须使用 abstract 修饰符,并且不能实例化。在下面的情况下,类必须是抽象类:1.类中包含一个明确声明的抽象方法;2.类的任何一个父类包含一个没有实现的抽象方法;3.类的直接父接口声明或者继承了一个抽象方法,并且该类没有声明或者实现该抽象方法。如下例所示:abstract class a1public int v1;abstract void test();abstract class a2 extends a1 public int v2;class Sample extends v2 void test() 因为包含一个抽象方法 test,类 v1必须被声明为抽象类。它的子类 v2继承
3、了抽象方法 test,但没有实现它,所以它 也必须声明为抽象类。然而,v2的子类 Sample 因为实现了 test,所以它不必声明为抽象的。注意: 如果试图创建一个抽象类的实例就会产生编译错误;如果一个类是非抽象类却包含一个抽象方法,就会产生编译错误;构造函数和静态函数以及 Final 修饰的函数不能使用abstract 修饰符;接口缺省为 abstract Static 修饰符通常,在创建类的实例时,每个实例都会创建自己实例变量。但是在变量的声明中可以使用 Static 修饰符,它表明该成员变量属于类本身,独立于类产生的任何对象。这种成员变量称为静态变量(静态属性) 。方法的声明中也可以使
4、用 static 修饰符,表明该方法从属于类本身。在静态成员函数中只能直接调用其它的静态成员函数或引用静态属性,否则会造成编译错误。静态成员函数中也不能使用this 或者 super,因为它们是和类的对象相关联的。静态属性和静态方法不需要创建实例就可以使用。典型的例子是 java.lang 包中的 System 类中的方法和变量。我们常用System.out.println(“message”);输出信息,并没有创建 System 的实例,是因为 println 方法声明为 System 类的静态方法,它不需创建实例就可以使用。下面的例子会帮助你更深刻的理解 Static 修饰符。注意各个代码
5、块的加载次序。Class Samplestatic int s1=3;static int s2;static void display() System.out.println(“s1=“+s1);System.out.println(“s2=“+s2);staticSystem.out.println(“static block“);S2=s1+1;public static void main(Stringargs)Sample.display();一旦这个类被调用,所有的静态变量都被初始化,s1被赋为3,然后运行 static 块,这将打印出一段消息,并且把 s2赋为s1+1,即4。然
6、后解释器调用 main 成员函数,它调用了成员函数display,该函数输出 s1和 s2的信息。运行结果如下:C:java Samplestatic blocks1=3s2=4通过上面的例子还可以看出,一个静态成员函数可以通过它所属的类名来调用。final 修饰符如果一个类是完全实现的,并且不再需要继承子类,则它可以声明为 Final 类。如果 final 类的名字出现在另一个类声明的extends 字句的后面就会产生编译错误。这表明 final 类不能又任何的子类。类不能同时被声明为 abstract 和 final,因为 abstract类中的 abstract 方法永远没有机会被实现。
7、在缺省情况下,所有的成员函数和实例变量都可以被覆盖。如果你希望你的变量或成员函数不再被子类覆盖,可以把它们声明为 final。例如:final int MAX_VALUE = 100; 表示,MAX_VALUE 的值为100,并且不能再更改。final 变量用大写标识符是一个一般的约定。二,访问控制符。访问控制符是一组限定类、域或方法是否可以被程序里的其他部分访问和调用的修饰符。类的访问控制符只有一个 public,域和方法的访问控制符有四个,分别是public、private、protected、private protected,另外还有一种没有定义专门的访问控制符的缺省情况。1,公有访问
8、控制符 public:Java 的类是通过包的概念来组织的,包氏类的一个松散的集合。处于同一个包中的类可以不需要任何说明而方便的互相访问和引用,而对于不同包中的类,则不行。但当一个类被声明为 public 时,他就具有了被其他包中的类访问的可能性,只要这些其他包中的类在程序中使用 import 语句引入 public 类,就可以访问和引用这个类。类中被设定为 public 的方法是这个类对外的接口部分,避免了程序的其他部分直接去操作类内的数据,这实际就是数据封装思想的体现。每个 Java 程序的主类都必须是 public 类,也是基于相同的原因。用 public 修饰的类的域称为公共域。如果公
9、共域属于公共类,则它能被所有的其他类所引用。public 修饰符会造成安全性的数据封装性下降,所以一般应减少 public 域的使用。2,缺省访问控制符:缺省访问控制权规定,该类只能被同一个包中的类访问和引用,而不可以被其他包中的类使用,这种访问特性又称为包访问性。同样道理,类内的域或方法如果美育访问控制符来限定,也就具有包访问性。简单说,定义在同一个程序中的所有类属于一个包。3,私有访问控制符 private :用 private 修饰得域或方法只能被该类自身所访问和修改,而且不能被任何其他类(包括该类的子类)来获取和引用。private 修饰符用来声明那些类的私有成员,它提供了最高的保护级
10、别。4,保护访问控制符 protected :用 protected 修饰的成员变量可以被三种类所引用:该类自身、与它在同一个包中的其它类、在其他包中的该类的子类。使用protected 修饰符的主要作用是允许其他包中该类的子类来访问父类的特定属性。5,私有保护访问控制符 private protected :用 private protected 修饰的成员变量可以被两种类访问和引用,一种是该类本身,一种是该类的所有子类。把同一个包内的非子类排除在可访问的范围之外,使得成员变量更专于具有明确继承关系的类,而不是松散地组合在一起的包。访问修饰符名称 说明 备注public 可以被所有类访问(使
11、用) public 类必须定义在和类名相同的同名文件中packag 可以被同一个包中的类访 默认的访问权限,可以省略此修饰符名称 说明 备注final 使用此修饰符的类不能够被继承abstract如果要使用 abstract 类,之前必须首先建一个继承abstract 类的新类,新类中实现 abstract 类中的抽象方法。类只要有一个 abstract 方法,类就必须定义为 abstract,但 abstract 类不一定非要保护 abstract 方法不可以下是对各修饰符使用方法的详解:一、static修饰属性,方法,代码块1、静态方法:使这个方法成为整个类所公有的方法,可以用 类名.方法
12、名 直接访问 e 问(使用) 关键字,可以定义在和public 类的同一个文件中private修饰内部类注意:static 修饰的方法,不能直接访问(可以通过组合方式访问)本类中的非静态(static)成员(包括方法和属性)本类的非静态(static)方法可以访问本类的静态成员(包括方法和属性) ,可以调用静态方法。静态方法要慎重使用。在静态方法中不能出现 this 关键字,因为这是针对对象而言的。java 中的 main 方法必须写成 static 的,因为,在类加载时无法创建对象,静态方法可以不通过对象调用。所以在类加载时就可以通过 main 方法入口来运行程序。 注意:父类中是静态方法,
13、子类中不能覆盖为非静态方法。在符合覆盖规则的前提下,在父子类中,父类中的静态方法可以被子类中的静态方法覆盖,但是没有多态。使用引用调静态方法,相当于使用引用的类型去调用静态方法。(在使用对象调用静态方法是其实是调用编译时类型的静态方法)2、静态属性:全类公有,称为类变量那么这个属性就可以用 类名.属性名 来访问(共有的类变量与对象无关,只和类有关)类加载:虚拟机通过 I/O 流把一个类的信息从字节码文件中读入虚拟机并保存起来 一个类只会加载一次类变量,会在加载时自动初始化,初始化规则和实例变量相同。注意:类中的实例变量是在创建对象时被初始化的,被 static 修饰的属性,也就是类变量,是在类
14、加载时被创建并进行初始化,类加载的过程是进行一次。也就是类变量只会被创建一次。3、初始代码块在定义属性的位置上,在任何方法之外,定义一个代码块 动态初始代码块:在初始化属性之前调用初始化代码块 静态初始代码块:在类加载时运行 static 只被运行一次,往往用作一个类的准备工作二、一个类在什么时候被加载?时机 (延迟加载,能不加载就不加载)(1)new 一个对象的时候,加载(2)没有创建对象,访问类中静态成员(方法和属性) ,加载(3)声明一个类的引用,不加载(4)创建子类,先加载父类,再加载子类(5)父类中的公开静态方法,子类继承,使用子类的类名调用此方法,加载父类class Superpu
15、blic static m()class Sub extends Super在主函数中运行以下代码:Sub.m(); /加载了父类之后,虚拟机已经知道 m()方法的调用了,就不会再加载子类,延迟加载(6)没有创建对象,访问类中静态常量(能计算出结果的常量,在编译的时候会用计算出来的结果替换表达式) ,不加载 没有创建对象,访问类中静态常量(不确定的值) ,加载 (7)CoreJava day16 三、设计模式(编程套路) GOF(Group Of Four)四人帮模式 23种1、单例模式 Singleton:class Aprivate static A a = new A(); /私有静态的
16、实例变量指向自己,在类加载时创建唯一对象public static A newInstance() /提供公开静态的访问点,回返唯一实例 return a; private A() /私有的构造方法,防止滥用2、不变模式 :便于实例共享,减少对存储空间的消耗String 类采用了不变模式字符串中的内容是不变的 String a1 = “123“; /系统会先去串池中找“123“,找到,就共享使用一个,没找到就在串池中创建一个String a2 = new String(“123“); /在堆空间中创建“123“池化的思想,把需要共享的数据放在池中(节省空间,共享数据)只有 String 类可以
17、用“”中的字面值创建对象。在 String 类中,以字面值创建时,会到串池空间中去查找,如果有就返回串池中字符串的地址,并把这个地址付给对象变量。如果没有则会在串池里创建一个字符串对象,并返回其地址付购对象变量,当另一个以字面值创建对象时则会重复上述过程。如果是 new 在堆空间中创建 String 类的对象,则不会有上述的过程。a2=a1.intern(); /返回字符串在串池中的引用消极方面:字符串连接“+” ,产生很多的中间对象StringBuffer 类,字符串是可变的s.append(“A“); /连接字符串,不创建中间对象大量字符串连接的时候用 StringBuffer 取代 St
18、ring四、final修饰变量,方法,类1、修饰变量被 fianl 修饰的变量就是常量(常量名大写) ,一旦赋值不能改变 修饰局部变量:修饰基本数据类型 - 变量的值不能改变修饰引用 - 引用只能指向固定的对象修饰实例变量:默认值不生效,可以再赋值有两次赋值机会:初始化变量的时候 final int a = 20; 对于直接在初始化时赋值,final 修饰符常和 static 修饰符一起使用,避免浪费空间构造方法中设置 this.a = a;但是不能同时使用这两种方法 在一个对象完成创建的时候,对象中的所有 final 属性必须都完成赋值类变量可以是 final 的,也有两次赋值机会 :定义变
19、量的时候就赋值 ; 静态初始代码块中 2、修饰类不能被继承 在树状单继承关系中,final 类是树叶节点在一个 final 类中的所有方法,默认都是 final 的注意:final,不能用来修饰构造方法。在父类中如果有常量属性,在子类中使用常量属性时是不会进行父类的类加载。静态常量如果其值可以确定,就不会加载该类,如果不能确定则会加载该常量所在的类。 class Superprivate final void m() /用 final 可以证明出 private 的方法不继承给子类 class Sub extends Superpublic void m() /不是方法的覆盖 3、修饰方法 不
20、能被子类覆盖 从面向对象的角度理解,可以保持操作的稳定性五、abstract 抽象的修饰类和方法 1、修饰类 - 抽象类不能创建对象,可以声明引用,并通过引用调用类中的方法主要用于被子类继承的,可以用父类引用指向子类对象2、修饰方法只有声明,没有实现,用“;”代替“ ” 需要子类继承实现(覆盖) 。如果一个类中有抽象方法,那么这个类必须是抽象类。抽象类中不一定有抽象方法注意:父类是抽象类,其中有抽象方法,子类继承父类,必须把父类中的所有抽象方法都实现(覆盖)了,子类才有创建对象的能力,否则子类也必须是抽象类。抽象类中可以有构造方法,是子类在构造子类对象时需要调用的父类(抽象类)的构造方法。抽象
21、类的合理性:没有抽象类的实例,只有抽象类子类的实例抽象方法,定义和实现分离 抽象(abstract)方法代表了某种标准,定义标准,定义功能,在子类中去实现功能(子类继承了父类并需要给出从父类继承的抽象方法的实现) 。方法一时间想不到怎么被实现,或有意要子类去实现而定义某种标准,这个方法可以被定义为抽象。(abstract) 六、三个修饰符都能修饰方法(不包含构造方法)1、构造方法在创建对象的时候使用,如果是 static,那么只会在加载类的时候调用一次构造方法不能被继承(final) ,谈不到覆盖,更不会由子类实现(abstract)2、final 和 abstract,private 和 a
22、bstract,static 和abstract,这些是不能放在一起的修饰符因为 abstract 修饰的方法是必须在其子类中实现(覆盖) ,才能以多态方式调用,以上修饰符在修饰方法时子类都覆盖不了这个方法。final 是不可以覆盖,private 是不能够继承到子类,所以也就不能覆盖。static 是可以覆盖的,但是在调用时会调用编译时类型的方法(引用类型的方法) ,因为调用的是父类的方法,而父类的方法又是抽象的方法,不能调用。所以上的修饰符不能放在一起。 相关使用实例1.private 修饰符的使用实例:class Testprivate int money;Test()money=200
23、0;private int getMoney()return money;public static void main(String args)Test te=new Test();te.money=3000; /合法int m=te.getMoney(); /合法System.out.println(“money=“+m);2.protected 使用实例class Aprotected int weight ;protected int f( int a,int b )/ 方法体class B /A 于 B 在同一包中void g()A a=new A();A.weight=100;/合
24、法A.f(3,4); /合法3.static 使用实例public class DataTypes static int i;static float j;static int k;public static void main(String args)int l;Integer a=new Integer(1000);i=a.intValue();j=a.floatValue(); k=a.parseInt(“2000“);l=Integer.parseInt(“3000“);System.out.println(a.toString();System.out.println(i);Syst
25、em.out.println(j);System.out.println(k);System.out.println(l);4.final 使用实例public class ConstantVariable static final int staticVar = 10 + 8; /静态变量staticVar 是一个常变量final int instanceVar = 100 + 8; /实例变量instanceVar 也是一个常变量/静态变量 notConstantVariable 不是常变量,因为其初始化表达式不是编译时常量表达式static final int notConstantVa
26、riable = (int)(Math.random() * 10);public class Test public static void main(String args)/对类 ConstantVariable 中常变量 staticVar 的引用System.out.println(“staticVar = “ + ConstantVariable.staticVar);/对类 ConstantVariable 中常变量 instanceVar 的引用System.out.println(“instanceVar = “ + new ConstantVariable().instanceVar);/对类 ConstantVariable 中静态变量notConstantVariable 的引用System.out.println(“notConstantVariable = “ + ConstantVariable.notConstantVariable);