访问权限控制(3):接口和实现、类的访问权限

一、接口和实现简单介绍

    访问权限的控制常被称为是具体实现的隐藏。把数据和方法包装进类中,以及具体实现的隐藏,常共同被称作是封装。其结果是一个同时带有特征和行为的数据类型。

    出于两个很重要的原因,访问权限控制将权限的边界划在了数据类型的内部。第一个原因是要设定客户端程序员可以使用和不可以使用的界限。可以在结构中建立自己的内部机制,而不必担心客户端程序员会偶然地将内部机制当作是它们可以使用的接口的一部分。

    这个原因直接引出了第二个原因,即将接口和具体实现进行分离。如果结构是用于一组程序之中,而客户端程序员除了可以向接口发送信息之外什么也不可以做的话,那么就可以随意更改所有不是public的东西(例如有包访问权限、protected和private的成员),而不会破坏客户端代码。

    为了清除起见,可能会采用一种将public成员置于开头,后面跟着protected,包访问权限和private成员的创建类的形式。这样做的好处是类的使用者可以从头读起,首先阅读对他们而言最为重要的部分(即public成员,因为可以从文件外部调用它们),等到遇见作为内部实现细节的非public成员时停止阅读:

public class OrganizedByAccess {
	public void pub1 () { /* ... */ }
	public void pub2 () { /* ... */ }
	public void pub3 () { /* ... */ }
	private void priv1 () { /* ... */ }
	private void priv2 () { /* ... */ }
	private void priv3 () { /* ... */ }
	private int i;
}

    这样做仅能使程序阅读起来稍微容易一些,因为接口和具体实现仍旧混在一起。也就是说,仍能看到源代码--实现部分,因为它就在类中。另外,javadoc所提供的注释文档功能降低了程序代码的可读性对客户端程序员的重要性。将接口展现给某个类的使用者实际上是类浏览器的任务。类浏览器是一种以非常有用的方式来查阅所有可用的类,并告诉你用它们可以做些什么(也就是显示出可用成员)的工具。在java中,用Web浏览器浏览JDK文档可以得到使用类浏览器的效果。

二、类的访问权限

        在java中,访问权限修饰词也可以用于确定库中的哪些类对于该库的使用者是可用的。如果希望某个类可以为某个客户端程序员所用,就可以通过把关键字public作用于整个类的定义来达到目的。这样做甚至可以控制客户端程序员是否能创建一个该类的对象。

    为了控制某个类的访问权限,修饰词必须出现于关键字class之前。因此可以像下面这样声明:public class widget{}现在如果库的名字是access,那么任何客户端程序员都可以通过下面的声明访问Widget:import access.Widget;或import access.* ;

·    然而,这里还有一些额外的限制:

  1. 每个编译单元(文件)都只能有一个public类。这表示,每个编译单元都有单一的公共接口,用public类来表现。该接口可以按要求包含众多的支持包访问权限的类。如果在某个编译单元内有一个以上的public类,编译器就会给出出错信息。
  2. public类的名字必须完全与含有该编译单元的文件名相匹配,包括大小写。所以对于Widget而言,文件的名称必须是Widget.java,而不是widget.java或WIDGET.java。如果不匹配,同样将得到编译时错误。
  3. 虽然不是很常用,但编译单元内完全不带public类也是可能的。在这种情况下,可以随意对文件命名。(尽管随意命名会使得人们在阅读和维护代码时产生混淆。)

    如果获取了一个在access内部的类,用来完成Widget或是其他在access中的public类所要执行的任务,将会出现什么样的情况呢?你不想自找麻烦去为客户端程序员创建说明文档,而且你认为不久可能会想要完全改变原有方案并将旧版本一起删除,代之以一种不同的版本。为了保留此灵活性,需要确保客户端程序员不会依赖于隐藏在access之中的任何特定实现细节。为了达到这一点,只需将关键字public从类中拿掉,这个类就拥有了包访问权限。(该类只可以用于该包之中。)

    在创建一个包访问权限的类时,仍旧是在将该类的域声明为private时才有意义--应尽可能地总是将域指定为私有的,但是通常来说,将与类(包访问权限)相同的访问权限赋予方法也是很合理的。既然一个有包访问权限的类通常只能被用于包内,那么如果对你有强制要求,再此种情况下,编译器会告诉你,你只需要将这样的类的方法设定为public就可以了。

    请注意,类既不可以是private的(这样会使得除该类之外,其他任何类不可以访问它),也不可以是protected的。所以对于类的访问权限,仅有两个选择:包访问权限或public。如果不希望其他任何人对该类拥有访问权限,可以把所有的构造器都指定为private,从而阻止任何人创建该类的对象,但是有一个例外,就是你在该类的static成员内部可以创建。下面是一个示例:

class Soup1 {
	private Soup1() {
	}

	public static Soup1 makeSoup() {
		return new Soup1();
	}
}

class Soup2 {
	private Soup2() {
	}

	private static Soup2 ps1 = new Soup2();

	public static Soup2 access() {
		return ps1;
	}

	public void f() {
	}
}

public class Lunch {
	void testPrivate() {
		// Soup1 soup=new Soup1();
	}

	void testStatic() {
		Soup1 soup = Soup1.makeSoup();
	}

	void testSingleton() {
		Soup2.access().f();
	}
}

    到目前为止,绝大多数方法均返回void或基本类型,所以定义

public static Soup1 makeSoup() {
		return new Soup1();
	}

    初看起来可能有点令人迷惑不解。方法名称(makeSounp)前面的词Soup1告知了该方法返回的东西。目前为止,这里经常是void,意思是它不返回任何东西。但是也可以返回一个对象引用,示例中就是这种情况。这个方法返回了一个对Soup1类的对象的引用。

    Soup1类和Soup2类展示了如何通过将所有的构造器指定为private来阻止直接创建某个类的实例。请一定要牢记,如果没有明确地至少创建一个构造器的话,就会帮你创建一个默认构造器。(不带有任何参数的构造器)。如果我们自己编写了默认的构造器,那么就不会自动创建它了。如果把该构造器指定为private,那么就谁也无法创建该类的对象了。但是现在别人该怎样使用这个类呢?上面的例子就给出了两种选择:在Soup1中,创建一个static方法,它创建一个新的Soup1对象并返回一个对它的引用。如果想要在返回引用之前在Soup1上做一些额外的工作,或是如果想要记录到底创建了多少个Soup1对象(可能要限制其数量),这种做法将会是大有裨益的。

    Soup2用到了所谓的设计模式,这种特定的模式被称为singleton(单例),这是因为你始终只能创建它的一个对象。Soup2类的对象是作为Soup2的一个static private成员而创建的,所以有且仅有一个,而且除非是通过public方法access(),否则是无法访问到它的。

    正如前面所提到的,如果没能为类访问权限指定一个访问修饰符,它就会默认得到包访问权限。这就意味着该类的对象可以由包内任何其他类来创建,但在包外则是不行的。(一定要记住,相同目录下的所有不具有明确package声明的文件,都被视作是该目录下默认包的一部分。)然而,如果该类的某个static成员是public的话,则客户端程序员仍旧可以调用该static成员,尽管他们并不能生成该类的对象。

 如果本文对您有很大的帮助,还请点赞关注一下。

发布了100 篇原创文章 · 获赞 2 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_40298351/article/details/104098587