Java中的修饰符说明(public,private,protected,default,final,abstract,static)

时间:2021-06-29 21:05:24
   public(公共访问权限):这是一个宽松的访问控制级别,如果一个成员(包括成员变量、方法和构造器等)或者一个外部类使用public访问控制符修饰,那么这个成员或外部类就可以被所有类 (注:在该类外部,若是类成员,则需要类调用成员或外部类;若是非static的类,则应先实例化后,对象对其调用) 访问,不管访问类和被访问类是否处于同一个包中,是否具有父子继承关系。

    private(当前类访问权限):如果类里的一个成员(包括成员变量、方法和构造器等)使用private访问控制符来修饰,则这个成员只能在当前类的内部被访问。很显然,这个访问控制符用于修饰成员变量最合适,使用它来修饰成员变量就可以把成员变量隐藏在该类内部。

    protected(子类访问权限):如果一个成员(包括成员变量、方法和构造器等)使用protected访问控制符修饰,那么这个成员既可以被同一个包中的其他类访问,也可以被不同包中的子类访问。在通常情况下,如果使用protected来修饰一个方法,通常是希望其子类来重写这个方法。

    default(包访问权限):如果类里的一个成员(包括成员变量、方法和构造器等)或者一个外部类不使用任何访问控制符修饰,就称它是包访问权限,default访问控制的成员或外部类可以被相同包下的其他类访问。

    final:可用于修饰类、变量和方法,用于表示它修饰的类、方法和变量不可改变。
    (1)final成员变量:对于final修饰的成员变量而言,一旦有了初始值,就不能重新赋值,如果既没有在定义成员变量时指定初始值,也没有在初始化块、构造器中为成员变量指定初始值,那么这些成员变量的值将一直是系统默认分配的 0、'\u0000'、false或null,这些成员变量也就完全失去了存在的意义。因此Java语法规定: final修饰的成员变量必须由程序员显式地指定初始值
    归纳起来,final修饰的类变量、实例变量能指定初始值的地方如下:
  • 类变量:必须在静态初始块中指定初始值或声明该类变量时指定初始值,而且只能在两个地方的其中之一指定。
  • 实例变量:必须在非静态初始化块、声明该实例变量或构造器中指定初始值,而且只能在三个地方的其中之一指定。
    (2)final局部变量:系统不会对局部变量进行初始化,局部变量必须由程序员显式初始化。因此使用final修饰局部变量时,既可以在定义时指定默认值,也可以不指定默认值。
        如果final修饰的局部变量在定义时没有指定默认值,则可以在后面代码中对该final变量赋初始值,但只能一次,不能重复赋值;如果final修饰符的局部变量在定义时已经指定默认值,则后面代码中不能再对该变量赋值。 注:final修饰形参的情形。因为形参在调用该方法时,由系统根据传入的参数来完成初始化,因此使用final修饰的形参不能被赋值。
    (3)final修饰基本类型变量和引用类型变量的区别
        当使用final修饰基本类型变量时,不能对基本类型变量重新赋值,因此基本类型变量不能被改变。但对于引用变量而言,它保存的仅仅是一个引用,final只保证这个引用类型变量所引用的地址不会改变,即一直引用同一个对象,但这个对象完全可以发生改变。
        下面程序示范了final修饰数组和Person对象的情形:
class Person
{
private int age;
public Person(){}
// 有参数的构造器
public Person(int age)
{
this.age = age;
}
// 省略age的setter和getter方法
// age的setter和getter方法
...
}
public class FinalReferenceTest
{
public static void main(String[] args)
{
// final修饰数组变量,iArr是一个引用变量
final int[] iArr = {5, 6, 12, 9};
System.out.println(Arrays.toString(iArr));
// 对数组元素进行排序,合法
Arrays.sort(iArr);
System.out.println(Arrays.toString(iArr));
// 对数组元素赋值,合法
iArr[2] = -8;
System.out.println(Arrays.toString(iArr));
// 下面语句对iArr重新赋值,非法
//iArr = null;
// final修饰Person变量,p是一个引用变量
final Person p = new Person(45);
// 改变Person对象的age实例变量,合法
p.setAge(23);
System.out.println(p.getAge());
// 下面语句对p重新赋值,非法
// p = null;
}
}
    (4)可执行“宏替换”的final变量
    对一个final变量来说,不管它是类变量、实例变量,还是局部变量,只要该变量满足三个条件,这个final变量就不再是一个变量,而是相当于一个直接量。
  • 使用final修饰符修饰。
  • 在定义该final变量时指定了初始值。
  • 该初始值可以在编译时就被确定下来。
    (5)final方法:
        final修饰的方法不可被重写,如果处于某些原因,不希望子类重写父类的某个方法,则可以使用final修饰该方法。Java提供的Object类里就有一个final方法:getClass(),因为Java不希望任何类重写这个方法,所以使用final把这个方法密封起来。但对于该类提供的toString()和equals()方法,都允许子类重写,因此没有使用final修饰它们。
    (6)final类:
    final修饰的类不可以有子类,例如java.lang.Math类就是一个final类,它不可以有子类。
    当子类继承父类时,将可以访问到父类内部数据,并可通过重写父类方法来改变父类方法的实现细节,这可能会导致一些不安全的因素。为了保证某个类不可被继承,则可以使用final修饰这个类。下面代码示范了final修饰的类不可被继承。
  public final class FinalClass{}
// 下面的类定义将出现编译错误
class Sub extends FinalClass{}
 
    (7)不可变类
    不可变类(immutable)类的意思是创建该类的实例后,该实例的实例变量时不可改变的。Java提供的8个包装类和java.lang.String类都是不可变类,当创建它们的实例后,其实例的实例变量不可改变。
    例如如下代码:
    Double d = new Double(6.5);
    String str = new String("Hello");
    上面程序创建了一个Double对象和一个String对象,并为这两个对象传入了6.5和“Hello”字符串作为参数,那么Double类和String类肯定需要实例变量来保存这两个参数,但程序无法修改者两个实例变量的值,因此Double和String类没有提供修改它们的方法。
    如果需要创建自定义的不可变类,可遵循如下规则。
  • 使用private和final修饰符来修饰gia类的成员变量。
  • 提供带参构造器,用于根据传入参数来初始化类里的成员变量。
  • 仅为该类的成员变量提供getter方法,不要为该类的成员变量提供setter方法,因为普通方法无法修改final修饰的成员变量。
  • 如果有必要,重写Object类的hashCode()和equals()方法。equals()方法根据关键成员变量来作为两个对象是否相等的标准,除此之外,还应该保证两个用equals()方法判断为相等的对象的hashCode()也相等。

    abstract(修饰抽象方法和抽象类):抽象方法和抽象类必须使用abstract修饰符来定义,有抽象方法的类的只能被定义成抽象类,抽象类里可以没有抽象方法。
    抽象方法和抽象类的规则如下:
  • 抽象类必须使用abstract修饰符,抽象方法也必须使用abstract修饰符来修饰,抽象方法不能有方法体。
  • 抽象类不能被实例化,无法使用new关键字来调用抽象类的构造器创建抽象类的实例。即使抽象类里不包含抽象方法,这个抽象类也不能创建实例。
  • 抽象类可以包含成员变量、方法(普通方法和抽象方法都可以)、构造器、初始化块、内部类(接口、枚举)5中成分。抽象类的构造器不能用于创建实例,主要是用于被其子类调用。
  • 含有抽象方法的类(包括直接定义了一个抽象方法;或继承了一个抽象父类,但没有完全实现父类包含的抽象方法;或实现了一个接口,但没有完全实现接口包含的抽象方法三种情况)只能被定义成抽象类。
    
     当使用abstract修饰类时,表明这个类只能被继承;当使用abstract修饰方法时,表明这个方法必须由子类提供实现(即重写)。而final修饰的类不能被继承,final修饰的方法不能被重写。因此final和abstract永远不能同时使用
    除此之外,当使用static修饰一个方法时,表明这个方法属于该类本身,即通过类就可以直接调用该方法,但如果该方法被定义成抽象方法,则将导致通过该类来调用该方法时出现错误(调用了一个没有方法体的方法肯定会引起错误)。因此static和abstract不能同时修饰某个方法,即没有所谓的类抽象方法。
    static和abstract并不是绝对互斥的,static和abstract虽然不能同时修饰某个方法,但它们可以同时修饰内部类。
    abstract关键字修饰的方法必须被其子类重写才有意义,否则这个方法将永远不会有方法体,因此abstract方法不能定义为private访问权限,即private和abstract不能同时修饰方法。
下面有一个例题:
Java中的修饰符说明(public,private,protected,default,final,abstract,static)
    static:它的真正作用就是区分成员变量、方法、内部类、初始化块这四种成员到底属于类本身还是属于实例。在类中定义的成员,static相当于一个标志,有static修饰的成员属于类本身,没有static修饰的成员属于该类的实例。
    静态初始化块:如果定义初始化块时使用了static修饰符,则这个初始化块变成了静态初始化块,也被称为类初始化块。类初始化块是类相关的,系统将在初始化阶段执行静态初始化块,而不是创建对象时才执行。因此静态初始化块比普通初始化块先执行。
    例如下题所示:
     Java中的修饰符说明(public,private,protected,default,final,abstract,static)

    静态内部类:如果使用static 来修饰一个内部类,则这个内部类就属于外部类本身,而不属于外部类的某个对象。因此使用static修饰的内部类,有的地方也称为静态内部类。
    注:static关键字的作用是把类的成员变成类相关,而不是实例相关,即static修饰的成员属于整个类,而不是单个对象。外部类的上一级程序单元是包。所以不可使用static修饰;而内部类的上一级单元式外部类,使用static修饰可以使内部类变成外部类相关,而不是外部类实例相关。因此static关键字不可修饰外部类,但可修饰内部类。
    静态内部类可以包含静态成员变量,也可以包含非静态成员。根据静态成员不能访问非静态成员的规则,静态内部类不能访问外部类的实例成员,只能访问外部类的类成员。即使是静态内部类的实例方法也不能访问外部类的实例。