JavaSE基础知识(十六)--Java的访问权限控制关键字(public、protected、private)

Java SE 是什么,包括哪些内容(十六)?

本文内容参考自Java8标准

1、Java访问权限修饰词(关键字)

Java中总共有三个访问权限修饰词:public、protected、private,但是却有四种访问权限。
因为如果不提供任何访问权限修饰词,那么就意味着,它是"包访问权限",因此无论如何,所有事物都具有某种形式的访问权限控制,在以下的内容中,我将介绍各种类型的访问权限。

⑴、包访问权限

我在这篇博文之前的博文中的代码示例中很少涉及访问权限修饰词,有的示例代码甚至没有使用任何的访问权限修饰词,因为默认的访问权限没有修饰词(关键字)。
这种情况通常指的就是包访问权限(有时也表示成friendly),这就意味着,当前包中的所有其他类(这个类指的是所有类,也就是不管是什么权限的类)对这个成员都有访问权限,但对于这个包之外的所有类,这个成员却是不可见的(权限就是后面要说要的private)。
由于一个编译单元(一个.java文件),只能隶属于一个包,所以经由包访问权限,处于同一个包中的所有编译单元的没有访问权限修饰词的类,都是可以被包中的其他任何类访问的。
包访问权限允许将包内所有相关的类组合起来,以使它们之间可以轻松地相互作用,当把类组织起来放在一个包内时,也就给它们的包访问权限的成员赋予了相互访问的权限,应该说包访问权限为把类聚在一个包中的做法提供了意义和理由。
类有权控制自己的哪些成员可以被其他的类访问,其它包内的类需要根据当前类内的成员的访问权限修饰词来决定是否能访问。同时,其他类如果需要访问当前类的成员,获得访问权的途径有以下几种:
1.该成员的访问权限修饰词是public,那么其他类无论在哪里,都可以访问该成员。
图示:
在包名为com.pojo.www的类Person中创建了两个权限修饰词为public的类变量。
JavaSE基础知识(十六)--Java的访问权限控制关键字(public、protected、private)_第1张图片
新建另外的一个包,尝试访问person类中的这两个变量:
JavaSE基础知识(十六)--Java的访问权限控制关键字(public、protected、private)_第2张图片
JavaSE基础知识(十六)--Java的访问权限控制关键字(public、protected、private)_第3张图片
隔着不同的项目是没有办法直接访问的!需要配置CLASSPATH!所以上文说的无论在哪里,涉及到了一个问题,如果隔着项目了,记得配置一下CLASSPATH。
2、通过不加访问权限修饰词并将其他相关的类放置在同一个包内的方式赋予成员包访问权,仅仅只是包内的其它类可以访问该成员
我们将第一点的图示内容进行修改,将两个类变量的public权限修饰词去掉,看下什么效果。
图示:
JavaSE基础知识(十六)--Java的访问权限控制关键字(public、protected、private)_第4张图片
在另外的一个包中,这两个变量立马就变得无法访问了:
JavaSE基础知识(十六)--Java的访问权限控制关键字(public、protected、private)_第5张图片
但是同一个包中的其它类还是可以直接访问的:
JavaSE基础知识(十六)--Java的访问权限控制关键字(public、protected、private)_第6张图片
3、有一个访问权限修饰词是专门为后面将要涉及到的继承而设计的:protected。被它修饰的内容,仅子类能访问,其他任何关系的类都不能访问,当然,子类同时也能访问权限修饰词为public的内容。
权限修饰词为private的内容,除了当前类内的其它部分能访问(比如方法,代码块等),其它任何类都不能访问,包括这个类的所有子类。
图示(能看懂的作为了解,看不懂的可以在我继承的博文中找答案):
还是将上面图示中的内容稍作修改,加上权限修饰词protected。
JavaSE基础知识(十六)--Java的访问权限控制关键字(public、protected、private)_第7张图片
现在来创建Person类的子类:
但是要分几种情况:
同一个包内的子类:
JavaSE基础知识(十六)--Java的访问权限控制关键字(public、protected、private)_第8张图片
同一个包内的子类是可以直接访问protected成员的!
不同包内的子类:
JavaSE基础知识(十六)--Java的访问权限控制关键字(public、protected、private)_第9张图片
不同包内的子类也是可以直接访问protected成员的!
当然,如果你想跨项目,必须将Person.class配置到CLASSPATH,以便其它项目中的类能继承,成为它的子类,那么也是可以访问的,这里就不上图了,有兴趣的可以自行尝试一下。
4、提供访问器和编译器,也就是接下来我的博文中将频繁使用的getset方法,以读取和改变数据,对OOP而言,这是最优雅的方式,而且,这也是JavaBean的基本原理。
实际上get和set方法解决了上面包访问权限的问题,直接导致了包访问权限失效了!
图示:
JavaSE基础知识(十六)--Java的访问权限控制关键字(public、protected、private)_第10张图片
JavaSE基础知识(十六)--Java的访问权限控制关键字(public、protected、private)_第11张图片

⑵、public :接口访问权限

用关键字public修饰的成员,就意味着声明自己对每个类都是可用的,尤其是客户端程序员更是如此!
代码示例:
假设定义了一个包含下面编译单元的dessert包:

// public权限修饰词
   //包名access.dessert
   package access.dessert;
   //类名Cookie
   public class Cookie{
      //public构造方法
      public Cookie(){
         //打印字符串"Cookie constructor"
         System.out.println("Cookie constructor");
      }
      //默认访问权限的方法bite()
      void bite(){
         //打印字符串"bite"
         System.out.println("bite");
      }
   }

注意,Cookie.java必须位于dessert子目录文件夹中,而该目录又必须位于access文件夹下,剩下的路径,根据具体的情况配置CLASSPATH就行。之前的博文我强调过:不要错误地认为Java总是将当前目录默认视为查找的路径之一,如果你的CLASSPATH里面缺少一个".",它是不会查找那里的。
如果你继续创建了一个使用类Cookie的程序:

// 使用类Cookie的程序
   //引入包access.dessert下的所有的类
   import access.dessert.*;
   //类Dinner
   public class Dinner{
      //程序执行入口main方法
      public static void main(String[] args) {
         //创建Cookie对象
         Cookie c = new Cookie();
         //不能访问方法,因为这个方法的权限是包访问权限,
         //而类Dinner显然与类Cookie显然不在同一个包中
         //c.bite();
      }
   }

从以上代码的结果来看,只能创建一个Cookie类,因为构造方法是public的,并且类也是public的,但是方法bite()只向包dessert的里的类提供访问权。显然类Dinner没有package关键字,不属于这个包。
默认包:
在之前的博文中提到过一个问题:一个.java的文件名称必须要和这个文件中public修饰的类的名称完全一致,并且这个文件中只能有一个public修饰的类,那么问题来了:

是不是每个.java文件中都必须有public修饰的类?
答案是:不一定,也可以一个都没有!

我们来看个示例:
图一:类Cake,没有package,即没有任何包名,也不是public修饰的类,但是可以编译,并且可以使用。
JavaSE基础知识(十六)--Java的访问权限控制关键字(public、protected、private)_第12张图片
**图二:**类Cake,没有package,即没有任何包名,也不是public修饰的类,但是可以编译,并且可以使用。
JavaSE基础知识(十六)--Java的访问权限控制关键字(public、protected、private)_第13张图片
图三: 以上两个类看起来没有任何关系,现在相互调用:可以运行得出结果:
JavaSE基础知识(十六)--Java的访问权限控制关键字(public、protected、private)_第14张图片
决定性的因素:
这两个类一定在同一个目录下面:
JavaSE基础知识(十六)--Java的访问权限控制关键字(public、protected、private)_第15张图片
你最初肯定会认为这两个文件毫不相关,但是类Cake却可以创建类Pie并且调用它的方法f()(注意,为了使文件可以被编译,在你的CLASSPATH下面一定要配置".")。
通常你会认为,Pie和f()享有包访问权限,但这只是部分正确的,Cake.java可以访问它们是因为它们同处于相同的目录并且没有为自己设定任何的包名称,Java会将这样的文件自动看作是隶属于该目录的默认包中,于是为该目录中的其它文件都提供了包访问权限。

⑶、private :你无法访问

关键字private的意思是:除了包含该成员的类之外,其它任何类都无法访问该成员,由于处于同一个包内的其他类是不能访问private成员的,这等于是自己隔离了自己(当前类的变量或方法将自己与其它的类隔离开来),从另一个方面来说,让许多人共同合作来创建一个包也是不太可能的,所以private关键字修饰的内容是允许你随意改变的,而不必考虑这样做会影响其它包的类,实际上private关键字正是类库程序员维护类库的时候对客户端程序不会产生任何影响的关键(因为private修饰的内容客户端程序员根本就用不到,它们仅仅是起支持当前类内其他部分的作用)。
实际上,默认的包访问权限已经提供了充足的隐藏措施,因为使用类的客户端程序员是无法访问包访问权限成员的:
图示:
JavaSE基础知识(十六)--Java的访问权限控制关键字(public、protected、private)_第16张图片
同一个包中的类可以访问它:
JavaSE基础知识(十六)--Java的访问权限控制关键字(public、protected、private)_第17张图片
不同包中的类不能访问它:
JavaSE基础知识(十六)--Java的访问权限控制关键字(public、protected、private)_第18张图片
实际上这样做很好,因为默认的包访问权限是我们经常使用的一种权限,同时也是一种在忘记添加任何访问权限控制时自动获得的一种权限。因此,通常考虑的是,哪些成员是想要明确公开给客户端程序员使用的,就将它们声明为public。但是在你最初接触Java的时候,可能不会认为自己经常要去使用关键字private,因为没有它,照样可以工作。然而,事实很快就会证明,对private的使用是多么的重要,尤其是在多线程的环境下:
代码示例:

// private关键字的使用
   //类Sundae
   class Sundae{
      //private修饰的构造方法
      private Sundae(){}
      //包访问权限的方法makeSundae()
      static Sundae makeSundae(){
        //返回一个Sundae对象
        return  new Sundae();
      } 
   }
   //类Sundae
   public class IceCream{
      //程序执行入口main方法
      public static void main(String[] args){
         //不能通过Sundae s = new Sundae();这种形式类创建对象了。
         //因为唯一的一个构造器是private修饰的,不可用
         //Sundae s = new Sundae();
         //通过调用makeSundae()方法,返回一个对象。
         Sundae s = Sundae.makeSundae();
      }
   }
   //到后面讨论模式的时候,你还会接触到以上private的用法,
   //因为有一种模式叫做单例模式,只允许创建一个对象.那么这个类的
   //构造方法都是private修饰的。

这是一个说明private终有其用武之地的示例,可能想控制如何创建对象,并且直接阻止别人访问某个构造方法(或全部构造方法),在上面的代码示例中,不能通过构造方法来创建对象,而必须调用方法makeSundae()来达到此目的。
任何你可以肯定只是一个类中类似助手角色的方法你都可以把它设置为private,以确保不会在包内的其他地方被误用,这对于类中的域同样重要,除非必须公开所有的底层实现细目(这种情况很罕见),否则就应该将所有的域都指定为private,然而,不能因为在某个类中某个对象的引用是private,就认为在其他类中不能拥有这个类的public引用,因为一个对象可以拥有多个引用。有的引用可以是private,有的引用可以是public。

⑷、protected :继承访问权限

要理解继承访问权限,我们在内容上需要做一点跳跃,因为,写到这里,我还未详细说明有关继承的内容,但是为了内容的完整性,还是提供了对使用protected的简要介绍。
关键字protected处理的是继承的理念,通过继承可以利用现有类(我们称之为基类)的所有内容为基础(新增域或者是方法等),在这个基础之上添加新的内容(域或者方法),甚至改变现有类中的某些内容(一般是改变方法的实现),从而形成一个新的类,落实到代码中就是通过关键字extends声明(通过extends现有类形成了一个新类):
代码示例:

// extends拓展现有类
   class Foo extends Bar{
   }

如果你创建了一个新的包,并自另一个包继承类,那么唯一可以访问的成员就是源包的public成员(当然,如果在同一个包中继承,那么就可以访问所有的包访问权限成员),有时,基类的创建者会希望某一个特定的成员,把对它的访问权限赋予派生类(也就是继承出来的类)而不是所有的类,这就需要关键字protected来完成这一工作,同时,protected也提供包访问权限,也就是说,相同包的其他类也可以访问protected成员。
代码示例:

  // 导入需要使用的包
  import access.dessert.*;
  //类Chocolateship继承(拓展)类Cookie
  //这个类Cookie的代码就是上面的类Cookie
  public class Chocolateship extends Cookie{
     //构造方法
     public Chocolateship(){
        //打印字符串"Chocolateship constructor"
        System.out.println("Chocolateship constructor");
     }
     //方法chomp()
     public void chomp(){
        //这里不能访问bite()
        //因为方法bite()是包访问权限,不是protected的继承权限
        //bite();
     }
     //程序执行入口main方法
     public static void main(String[] args){
         //创建类Chocolateship对象
         Chocolateship c = new Chocolateship();
         //调用方法chomp()
         c.chomp();
     }
  }

你会发现继承技术一个很有趣的事情,如果类Cookie中有一个方法bite(),那么该方法也同时存在于任何一个从Cookie继承的类中,但是由于方法bite()是包访问权限,又位于另一个包中,所以在从Cookie类继承的不同包的类中是不能访问的,当然,你可以把它指定为public,但是这样做,所有人都会拥有对它的访问权限,很可能这并不是你希望的,所以,如果我们将类Cookie做如下修改:
代码示例:

// public权限修饰词
   //包名access.dessert
   package access.dessert;
   //类名Cookie
   public class Cookie{
      //public构造方法
      public Cookie(){
         //打印字符串"Cookie constructor"
         System.out.println("Cookie constructor");
      }
      //void bite(){
         //打印字符串"bite"
         // System.out.println("bite");
      //将原先的默认访问权限修改为protected访问权限
      protected void bite(){
         //打印字符串"bite"
         System.out.println("bite");
      }
   }

现在方法bite()对于所有继承自Cookie的类而言,都是可用的了。

  // 导入需要使用的包
  import access.dessert.*;
  //类Chocolateship继承(拓展)类Cookie
  //这个类Cookie的代码就是上面的类Cookie
  public class Chocolateship extends Cookie{
     //构造方法
     public Chocolateship(){
        //打印字符串"Chocolateship constructor"
        System.out.println("Chocolateship constructor");
     }
     //方法chomp()
     public void chomp(){
       //可以直接使用了,尽管在不同的包中
       bite();
     }
     //程序执行入口main方法
     public static void main(String[] args){
         //创建类Chocolateship对象
         Chocolateship c = new Chocolateship();
         //调用方法chomp()
         c.chomp();
     }
  }

最后提示一点:尽管方法bite()也是包访问权限,但是它不是public的!
PS:时间有限,有关Java SE的内容会持续更新!今天就先写这么多,如果有疑问或者有兴趣,可以加QQ:2649160693,并注明CSDN,我会就博文中有疑义的问题做出解答。同时希望博文中不正确的地方各位加以指正!

你可能感兴趣的:(Java,SE)