💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
## [类访问权限](https://lingcoder.gitee.io/onjava8/#/book/07-Implementation-Hiding?id=%e7%b1%bb%e8%ae%bf%e9%97%ae%e6%9d%83%e9%99%90) 访问权限修饰符也可以用于确定类库中的哪些类对于类库的使用者是可用的。如果希望某个类可以被客户端程序员使用,就把关键字**public**作用于整个类的定义。这甚至控制着客户端程序员能否创建类的对象。 为了控制一个类的访问权限,修饰符必须出现在关键字**class**之前: ~~~ public class Widget { ~~~ 如果你的类库名是**hiding**,那么任何客户端程序员都可以通过如下声明访问**Widget**: ~~~ import hiding.Widget; ~~~ 或者 ~~~ import hiding.*; ~~~ 这里有一些额外的限制: 1. 每个编译单元(即每个文件)中只能有一个**public**类。这表示,每个编译单元有一个公共的接口用**public**类表示。该接口可以包含许多支持包访问权限的类。一旦一个编译单元中出现一个以上的**public**类,编译就会报错。 2. **public**类的名称必须与含有该编译单元的文件名相同,包括大小写。所以对于**Widget**来说,文件名必须是**Widget.java**,不能是**widget.java**或**WIDGET.java**。再次强调,如果名字不匹配,编译器会报错。 3. 虽然不是很常见,但是编译单元内没有**public**类也是可能的。这时可以随意命名文件(尽管随意命名会让代码的阅读者和维护者感到困惑)。 如果是一个在**hiding**包中的类,只用来完成**Widget**或**hiding**包下一些其他**public**类所要执行的任务,怎么设置它的访问权限呢? 你不想自找麻烦为客户端程序员创建说明文档,并且你认为不久后会完全改变原有方案并将旧版本删除,替换成新版本。为了保留此灵活性,需要确保客户端程序员不依赖隐藏在**hiding**中的任何特定细节,那么把**public**关键字从类中去掉,给予它包访问权限,就可以了。 当你创建了一个包访问权限的类,把类中的属性声明为**private**仍然是有意义的——应该尽可能将所有属性都声明为**private**,但是通常把方法声明成与类(包访问权限)相同的访问权限也是合理的。一个包访问权限的类只能被用于包内,除非强制将某些方法声明为**public**,这种情况下,编译器会告诉你。 注意,类既不能是**private**的(这样除了该类自身,任何类都不能访问它),也不能是**protected**的。所以对于类的访问权限只有两种选择:包访问权限或者**public**。为了防止类被外界访问,可以将所有的构造器声明为**private**,这样只有你自己能创建对象(在类的 static 成员中): ~~~ // hiding/Lunch.java // Demonstrates class access specifiers. Make a class // effectively private with private constructors: class Soup1 { private Soup1() {} public static Soup1 makeSoup() { // [1] return new Soup1(); } } class Soup2 { private Soup2() {} private static Soup2 ps1 = new Soup2(); // [2] public static Soup2 access() { return ps1; } public void f() {} } // Only one public class allowed per file: public class Lunch { void testPrivate() { // Can't do this! Private constructor: //- Soup1 soup = new Soup1(); } void testStatic() { Soup1 soup = Soup1.makeSoup(); } void testSingleton() { Soup2.access().f(); } } ~~~ 可以像 \[1\] 那样通过**static**方法创建对象,也可以像 \[2\] 那样先创建一个静态对象,当用户需要访问它时返回对象的引用即可。 到目前为止,大部分的方法要么返回 void,要么返回基本类型,所以 \[1\] 处的定义乍看之下会有点困惑。方法名(**makeSoup**)前面的**Soup1**表明了方法返回的类型。到目前为止,这里经常是**void**,即不返回任何东西。然而也可以返回对象的引用,就像这里一样。这个方法返回了对**Soup1**类对象的引用。 **Soup1**和**Soup2**展示了如何通过将你所有的构造器声明为**private**的方式防止直接创建某个类的对象。记住,如果你不显式地创建构造器,编译器会自动为你创建一个无参构造器(没有参数的构造器)。如果我们编写了无参构造器,那么编译器就不会自动创建构造器了。将构造器声明为**private**,那么谁也无法创建该类的对象了。但是现在别人该怎么使用这个类呢?上述例子给出了两个选择。在**Soup1**中,有一个**static**方法,它的作用是创建一个新的**Soup1**对象并返回对象的引用。如果想要在返回引用之前在**Soup1**上做一些额外操作,或是记录创建了多少个**Soup1**对象(可以用来限制数量),这种做法是有用的。 **Soup2**用到了所谓的*设计模式*(design pattern)。这种模式叫做*单例模式*(singleton),因为它只允许创建类的一个对象。**Soup2**类的对象是作为**Soup2**的**static****private**成员而创建的,所以有且只有一个,你只能通过**public**修饰的`access()`方法访问到这个对象。