一、访问修饰符
1.顾名思义,"访问修饰符"就是和访问权限有关得修饰符。
JAVA 的类(外部类)有 2 种访问权限: public、default。
而方法和变量有 4 种:public、default、protected、private。
其中默认访问权限和 protected 很相似,有着细微的差别。
public 意味着任何地方的其他类都能访问。
default 则是同一个包的类可以访问。
protected 表示同一个包的类可以访问,其他的包的该类的子类也可以访问。
private 表示只有自己类能访问。
2.这里重点说一下受保护的访问修饰符-protected
protected 需要从以下两个点来分析说明:
子类与基类在同一包中:被声明为 protected 的变量、方法和构造器能被同一个包中的任何其他类访问;
子类与基类不在同一包中:那么在子类中,子类实例可以访问其从基类继承而来的 protected 方法,而不能访问基类实例的protected方法。
package p1;public classFather1 {protected void f() {} //父类Father1中的protected方法
}
package p1;public classSon1 extends Father1 {}
package p11;public classSon11 extends Father1{}
package p1;public classTest1 {public static voidmain(String[] args) {
Son1 son1= newSon1();
son1.f();//Compile OK ----(1)
son1.clone(); //Compile Error ----(2)
Son11 son= newSon11();
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;classMyObject2 {protected voidsay() {
System.out.println("我是父类的say方法!")
}
}
package p22;public classTest2 extends MyObject2 {public static voidmain(String args[]) {
MyObject2 obj= newMyObject2();
obj.say();//Compile Error ----(1)
Test2 tobj= newTest2();
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 classDemo03_Static {//定义静态属性和静态方法
public static int a = 1;public static voidadd() {
a++;
}//定义成员属性和方法
public int b = 1;public voidadd1(){
b++;
}
}
public classDemo03_Static2 {public static voidmain(String[] args) {
//调用静态方法改变静态属性
Demo03_Static d1 = newDemo03_Static();
d1.add();//本来应该使用Demo03_Static.add();来调用静态方法这里为了方便理解没有使用
System.out.println(d1.a);//本来应该使用Demo03_Static.a;来调用静态属性
Demo03_Static d2= newDemo03_Static();
d2.add();//同上
System.out.println(d2.a);//调用成员方法改变成员属性
System.out.println("----------------------------------");
Demo03_Static d3= newDemo03_Static();
d3.add1();
System.out.println(d3.b);
Demo03_Static d4= newDemo03_Static();
d4.add1();
System.out.println(d4.b);
}
}
运行结果:
※⑦.父类中的静态方法可以被继承、但不能被子类重写。
※⑧.如果在子类中写一个和父类中一样的静态方法,那么该静态方法由该子类特有,两者不构成重写关系。
public classDemo03_Static {//定义静态属性和方法
public static int a = 1;public static voidadd() {
a++;
System.out.println("父类的静态方法!");
}
}
public class Demo03_StaticSon extendsDemo03_Static{public static void add(){//√,是子类自己的静态方法,和父类的方法不构成重写
System.out.println("调用子类和父类同名的静态方法");}/*public void add(){//×,不能和父类静态方法重名
}*/
public static voidmain(String[] args) {
Demo03_Static d= newDemo03_StaticSon();
d.add();//测试能否重写父类的静态方法(多态本来是调用子类重写的父类方法,但是若调用父类自己的方法,说明没有重写该方法);}
}
运行结果:
3.final
可以修饰:方法 属性 局部变量 类(非抽象类)
1.final修饰的类为最终类,不能被继承
2.final修饰的方法不能被重写,但可以被继承
3.final修饰的变量不能被修改为常量
4.final以及final static修饰的变量的初始化方式:
//-----------------成员变量------------------//
//初始化方式一,在定义变量时直接赋值
private final int i = 3;//初始化方式二,声明完变量后在构造方法中为其赋值//如果采用用这种方式,那么每个构造方法中都要有j赋值的语句
private final intj;publicFinalTest() {
j= 3;
}//如果取消该构造方法的注释,程序就会报错,因此它没有为j赋值
/*public FinalTest1(String str) {
}*/
//为了方便我们可以这样写
publicFinalTest(String str) {this(); //调用无参构造器
}//下面的代码同样会报错,因为对j重复赋值
/*public FinalTest1(String str1, String str2) {
this();
j = 3;
}*/
//初始化方式三,声明完变量后在构造代码块中为其赋值//如果采用此方式,就不能在构造方法中再次为其赋值//构造代码块中的代码会在构造函数之前执行,如果在构造函数中再次赋值,//就会造成final变量的重复赋值
private final intk;
{
k= 4;
}//-----------------类变量(静态变量)------------------//
//初始化方式一,在定义类变量时直接赋值
public final static int p = 3;//初始化方式二,在静态代码块中赋值//成员变量可以在构造函数中赋值,但是类变量却不可以。//因此成员变量属于对象独有,每个对象创建时只会调用一次构造函数,//因此可以保证该成员变量只被初始化一次;//而类变量是该类的所有对象共有,每个对象创建时都会对该变量赋值//这样就会造成变量的重复赋值。
public final static intq;static{
q= 3;
}