1. Overview
1.概述
In this quick article, we’ll cover the public modifier in-depth, and we’ll discuss when and how to use it with classes and members.
在这篇快速文章中,我们将深入介绍public修饰符,我们将讨论何时以及如何对类和成员使用它。
Additionally, we’ll illustrate the drawbacks of using public data fields.
此外,我们将说明使用公共数据字段的弊端。
For a general overview of access modifiers, definitely have a look at our article on Access Modifiers in Java.
关于访问修饰符的总体概述,请一定要看看我们的文章:Java中的访问修饰符。
2. When to Use the Public Access Modifier
2.何时使用公共访问修改器
Public classes and interfaces, along with public members, define an API. It’s that part of our code that others can see and use to control the behavior of our objects.
公共类和接口,以及公共成员,定义了一个API。它是我们代码的一部分,其他人可以看到并用来控制我们对象的行为。
However, overusing the public modifier violates the Object-Oriented Programming (OOP) encapsulation principle and has a few downsides:
然而,过度使用public修饰符违反了面向对象编程(OOP)的封装原则,并有一些缺点。
- It increases the size of an API, making it harder for clients to use
- It’s becoming harder to change our code because clients rely on it — any future changes might break their code
3. Public Interfaces and Classes
3.公共接口和类
3.1. Public Interfaces
3.1.公共接口
A public interface defines a specification that can have one or more implementations. These implementations can be either provided by us or written by others.
一个公共接口定义了一个可以有一个或多个实现的规范。这些实现既可以由我们提供,也可以由其他人编写。
For example, the Java API exposes the Connection interface to define database connection operations, leaving actual implementation to each vendor. At run-time, we get the desired connection based on the project setup:
例如,Java API暴露了Connection接口来定义数据库连接操作,而将实际的实现留给每个供应商。在运行时,我们根据项目设置获得所需的连接。
Connection connection = DriverManager.getConnection(url);
The getConnection method returns an instance of a technology-specific implementation.
getConnection方法返回一个特定技术实现的实例。
3.2. Public Classes
3.2.公共类
We define public classes so that clients can use their members by instantiation and static referencing:
我们定义公有类,以便客户可以通过实例化和静态引用来使用其成员。
assertEquals(0, new BigDecimal(0).intValue()); // instance member
assertEquals(2147483647, Integer.MAX_VALUE); // static member
Moreover, we can design public classes for inheritance by using the optional abstract modifier. When we’re using the abstract modifier, the class is like a skeleton that has fields and pre-implemented methods that any concrete implementation can use, in addition to having abstract methods that each subclass needs to implement.
此外,我们可以通过使用可选的abstract修饰符来设计用于继承的公共类。当我们使用abstract修饰符时,这个类就像一个骨架,它有字段和预先实现的方法,任何具体的实现都可以使用,此外还有每个子类需要实现的抽象方法。
For example, the Java collections framework provides the AbstractList class as a basis for creating customized lists:
例如,Java集合框架提供了AbstractList类作为创建自定义列表的基础。
public class ListOfThree<E> extends AbstractList<E> {
@Override
public E get(int index) {
//custom implementation
}
@Override
public int size() {
//custom implementation
}
}
So, we only have to implement the get() and size() methods. Other methods like indexOf() and containsAll() are already implemented for us.
因此,我们只需要实现get()和size()方法。其他方法如indexOf()和containsAll()已经为我们实现。
3.3. Nested Public Classes and Interfaces
3.3.嵌套公有类和接口
Similar to public top-level classes and interfaces, nested public classes and interfaces define an API datatype. However, they are particularly useful in two ways:
与公共顶层类和接口类似,嵌套的公共类和接口定义了一个API数据类型。然而,它们在两个方面特别有用。
- They indicate to the API end user that the enclosing top-level type and its enclosed types have a logical relationship and are used together
- They make our codebase more compact by reducing the number of source code files that we would’ve used if we’d declared them as top-level classes and interfaces
An example is the Map.Entry interface from the core Java API:
一个例子是来自Java核心API的Map.Entry接口。
for (Map.Entry<String, String> entry : mapObject.entrySet()) { }
Making Map.Entry a nested interface strongly relates it to the java.util.Map interface and has saved us from creating another file inside the java.util package.
将Map.Entry作为一个嵌套接口,将其与java.util.Map接口紧密联系起来,使我们不必在java.util包内创建另一个文件。
Please read the nested classes article for more details.
请阅读嵌套类文章了解更多细节。
4. Public Methods
4.公共方法
Public methods enable users to execute ready-made operations. An example is the public toLowerCase method in the String API:
公共方法使用户能够执行现成的操作。一个例子是String API中的公共toLowerCase方法。
assertEquals("alex", "ALEX".toLowerCase());
We can safely make a public method static if it doesn’t use any instance fields. The parseInt method from the Integer class is an example of a public static method:
如果一个公共方法不使用任何实例字段,我们就可以安全地将它变成静态方法。来自Integer类的parseInt方法就是一个公共静态方法的例子。
assertEquals(1, Integer.parseInt("1"));
Constructors are usually public so that we can instantiate and initialize objects, although sometimes they might be private like in singletons.
构造函数通常是公开的,这样我们就可以对对象进行实例化和初始化,尽管有时它们可能是私有的,比如在singletons。
5. Public Fields
5.公共领域
Public fields allow changing the state of an object directly. The rule of thumb is that we shouldn’t use public fields. There are several reasons for this, as we’re about to see.
公有字段允许直接改变一个对象的状态。经验法则是我们不应该使用公共字段。这有几个原因,我们即将看到。
5.1. Thread-Safety
5.1.线程安全
Using public visibility with non-final fields or final mutable fields is not thread-safe. We can’t control changing their references or states in different threads.
对非最终字段或最终可变字段使用公共可见性并不是线程安全的。我们无法控制在不同的线程中改变它们的引用或状态。
Please check our article on thread-safety to learn more about writing thread-safe code.
请查看我们关于thread-safety的文章,以了解有关编写线程安全代码的更多信息。
5.2. Taking Actions on Modifications
5.2.对修改采取行动
We have no control over a non-final public field because its reference or state can be set directly.
我们无法控制一个非最终的公共字段,因为它的引用或状态可以直接被设置。
Instead, it’s better to hide the fields using a private modifier and use a public setter:
相反,最好使用一个私有修改器来隐藏字段,并使用一个公共设置器。
public class Student {
private int age;
public void setAge(int age) {
if (age < 0 || age > 150) {
throw new IllegalArgumentException();
}
this.age = age;
}
}
5.3. Changing the Data Type
5.3.改变数据类型
Public fields, mutable or immutable, are part of the client’s contract. It’s harder to change the data representation of these fields in a future release because clients may need to refactor their implementations.
公共字段,可变或不可变,是客户契约的一部分。在未来的版本中,要改变这些字段的数据表示是比较困难的,因为客户可能需要重构他们的实现。
By giving fields private scope and using accessors, we have the flexibility to change the internal representation while maintaining the old data type as well:
通过赋予字段私有范围和使用访问器,我们可以灵活地改变内部表示,同时也保持旧的数据类型。
public class Student {
private StudentGrade grade; //new data representation
public void setGrade(int grade) {
this.grade = new StudentGrade(grade);
}
public int getGrade() {
return this.grade.getGrade().intValue();
}
}
The only exception for using public fields is the use of static final immutable fields to represent constants:
使用公共字段的唯一例外是使用静态最终不可变的字段来表示常量。
public static final String SLASH = "/";
6. Conclusion
6.结语
In this tutorial, we saw that the public modifier is used to define an API.
在本教程中,我们看到public修饰符被用来定义一个API。
Also, we described how overusing this modifier may restrict the ability to introduce improvements to our implementation.
另外,我们还描述了过度使用这个修饰语可能会限制对我们的实现进行改进的能力。
Finally, we discussed why it’s a bad practice to use public modifiers for fields.
最后,我们讨论了为什么对字段使用公共修饰符是一种不好的做法。
And, as always, the code samples of this article are available over on GitHub.
而且,像往常一样,本文的代码样本可以在GitHub上获得。