Java修饰符
一、访问修饰符
1.顾名思义,"访问修饰符"就是和访问权限有关得修饰符。
JAVA 的类(外部类)有 2 种访问权限: public、default。
而方法和变量有 4 种:public、default、protected、private。
其中默认访问权限和 protected 很相似,有着细微的差别。
- public 意味着任何地方的其他类都能访问。
- default 则是同一个包的类可以访问。
- protected 表示同一个包的类可以访问,其他的包的该类的子类也可以访问。
- private 表示只有自己类能访问。
2.这里重点说一下受保护的访问修饰符-protected
protected 需要从以下两个点来分析说明:
-
子类与基类在同一包中:被声明为 protected 的变量、方法和构造器能被同一个包中的任何其他类访问;
-
子类与基类不在同一包中:那么在子类中,子类实例可以访问其从基类继承而来的 protected 方法,而不能访问基类实例的protected方法。
package p1; public class Father1 { protected void f() {} // 父类Father1中的protected方法 } package p1; public class Son1 extends Father1 {} package p11; public class Son11 extends Father1{} package p1; public class Test1 { public static void main(String[] args) { Son1 son1 = new Son1(); son1.f(); // Compile OK ----(1) son1.clone(); // Compile Error ----(2) Son11 son = new Son11(); son11.f(); // Compile OK ----(3) son11.clone(); // Compile Error ----(4) } }
对于上面的示例,首先看(1)(3),其中的f()方法从类Father1继承而来,其可见性是包p1及其子类Son1和Son11,而由于调用f()方法的类Test1所在的包也是p1,因此(1)(3)处编译通过。其次看(2)(4),其中的clone()方法的可见性是java.lang包及其所有子类,对于语句"son1.clone();"和"son11.clone();",二者的clone()是继承了Object类的方法,在类Son1、Son11中是可见的,但对Test1是不可见的,因此(1)(3)处编译不通过。
package p2; class MyObject2 { protected void say() { System.out.println("我是父类的say方法!") } } package p22; public class Test2 extends MyObject2 { public static void main(String args[]) { MyObject2 obj = new MyObject2(); obj.say(); // Compile Error ----(1) Test2 tobj = new Test2(); tobj.say(); // Complie OK ----(2) } }
对于(1)而言,say()方法来自于类MyObject2本身,因此其可见性为包p2及MyObject2的子类,虽然Test2是MyObject2的子类,但在Test2中不能访问基类MyObject2的protected方法say(),因此编译不通过;对于(2)而言,由于在Test2中访问的是其本身实例的从基类MyObject2继承来的的say(),因此编译通过。
3.访问修饰符中一些值得注意得地方:
Private 访问修饰符的使用主要用来隐藏类的实现细节和保护类的数据;
被声明为 public 的类、方法、构造方法和接口能够被任何其他类访问;
protected 访问修饰符不能修饰类和接口,方法和成员变量能够声明为 protected,但是接口的成员变量和成员方法不能声明为 protected;
二、非访问修饰符
1.abstract
可以修饰:方法 类 接口
【抽象类】
①.使用abstract关键字修饰类,称为抽象类
②.抽象类不能被实例化
【抽象方法】
①.被abstract修饰的没有方法体的方法
②.非抽象类继承抽象类,那么子类必须重写父类所有的抽象方法
③.抽象方法必须在抽象类中,但抽象类中不都是抽象方法
【接口】
①接口定义时使用interface关键字,默认用abstract修饰此关键字了;
②接口里的方法即使不写abstract依然是抽象方法,因为默认使用abstract修饰
2.static
可以修饰: 方法 属性 内部类(不可修饰外部类)
①.static修饰的属性 (静态属性)也叫类属性
②.static修饰的方法(静态方法)也叫类方法
③.推荐使用:类名.属性名 类名.方法名()来调用 (也可以使用对象调用)但不推荐
④.类属性和类方法是属于类的,在类加载时就加载,成员属性和成员方法在对象创建时才加载
因此静态属性和静态方法先于非静态属性和方法, 所以静态方法中不能调用非静态属性,非静态方法可以调用静态属性
⑤.静态方法不能使用this,super关键字;因为他两个代指子类对象和父类对象而静态比对象加载的早
⑥.由于类属性和类方法是属于类的,因此只会在类装载时产生一份,因此该类的多个实例只能使用同一个静态属性和方法(下面代码辅助理解该条)
public class Demo03_Static { //定义静态属性和静态方法 public static int a = 1 ; public static void add() { a++; } //定义成员属性和方法 public int b = 1; public void add1(){ b++; } }
public class Demo03_Static2 { public static void main(String[] args) { //调用静态方法改变静态属性 Demo03_Static d1 = new Demo03_Static(); d1.add();//本来应该使用Demo03_Static.add();来调用静态方法这里为了方便理解没有使用 System.out.println(d1.a);//本来应该使用Demo03_Static.a;来调用静态属性 Demo03_Static d2 = new Demo03_Static(); d2.add();//同上 System.out.println(d2.a); //调用成员方法改变成员属性 System.out.println("----------------------------------"); Demo03_Static d3 = new Demo03_Static(); d3.add1(); System.out.println(d3.b); Demo03_Static d4 = new Demo03_Static(); d4.add1(); System.out.println(d4.b); } }
运行结果:
※⑦.父类中的静态方法可以被继承、但不能被子类重写。
※⑧.如果在子类中写一个和父类中一样的静态方法,那么该静态方法由该子类特有,两者不构成重写关系。
public class Demo03_Static { //定义静态属性和方法 public static int a = 1 ; public static void add() { a++; System.out.println("父类的静态方法!"); } }
public class Demo03_StaticSon extends Demo03_Static{ public static void add(){//√,是子类自己的静态方法,和父类的方法不构成重写 System.out.println("调用子类和父类同名的静态方法"); } /*public void add(){//×,不能和父类静态方法重名
}*/ public static void main(String[] args) { Demo03_Static d = new Demo03_StaticSon(); d.add();//测试能否重写父类的静态方法(多态本来是调用子类重写的父类方法,但是若调用父类自己的方法,说明没有重写该方法); } }
运行结果:
3. final
可以修饰:方法 属性 局部变量 类(非抽象类)
1.final修饰的类为最终类,不能被继承
2.final修饰的方法不能被重写,但可以被继承
3.final修饰的变量不能被修改为常量
4.final以及final static修饰的变量的初始化方式:
//-----------------成员变量------------------// //初始化方式一,在定义变量时直接赋值 private final int i = 3; //初始化方式二,声明完变量后在构造方法中为其赋值 //如果采用用这种方式,那么每个构造方法中都要有j赋值的语句 private final int j; public FinalTest() { j = 3; } //如果取消该构造方法的注释,程序就会报错,因此它没有为j赋值 /*public FinalTest1(String str) { }*/ //为了方便我们可以这样写 public FinalTest(String str) { this(); //调用无参构造器 } //下面的代码同样会报错,因为对j重复赋值 /*public FinalTest1(String str1, String str2) { this(); j = 3; }*/ //初始化方式三,声明完变量后在构造代码块中为其赋值 //如果采用此方式,就不能在构造方法中再次为其赋值 //构造代码块中的代码会在构造函数之前执行,如果在构造函数中再次赋值, //就会造成final变量的重复赋值 private final int k; { k = 4; } //-----------------类变量(静态变量)------------------// //初始化方式一,在定义类变量时直接赋值 public final static int p = 3; //初始化方式二,在静态代码块中赋值 //成员变量可以在构造函数中赋值,但是类变量却不可以。 //因此成员变量属于对象独有,每个对象创建时只会调用一次构造函数, //因此可以保证该成员变量只被初始化一次; //而类变量是该类的所有对象共有,每个对象创建时都会对该变量赋值 //这样就会造成变量的重复赋值。 public final static int q; static { q = 3; }