JavaRush /Java 博客 /Random-ZH /喝咖啡休息#165。Java 中的包。适合初学者的线程安全方法

喝咖啡休息#165。Java 中的包。适合初学者的线程安全方法

已在 Random-ZH 群组中发布

Java包

来源: Usemynotes 这篇文章将帮助您更好地理解 Java 中的包、它们的用途以及如何实现它们。 喝咖啡休息#165。 Java 中的包。 适合初学者的线程安全方法 - 1

Java中什么是包

Java 中的包是将一组类、接口和子包组合在一起的一种方式。包用于创建相关类、接口、枚举等组。 子包是另​​一个包内的包。默认情况下不会导入它们,但如果需要,您可以手动导入它们。访问规范不提供给子包的各个成员;它们被视为单独的包。

Java 中的一些类型的包:

  • java.lang - 默认情况下与 Java 捆绑在一起。
  • java.io - 包含与输入/输出相关的类、方法和其他元素。

为什么需要包?

  • 以避免名称冲突。
  • 提供受控访问。
  • 实现数据封装。

如何在Java中创建包?

让我们创建一个名为Computer的包。通常,包名称以小写字母书写。这样做只是为了避免与类名发生名称冲突。我们还将创建一个名为Pluggable 的接口,该接口位于计算机包中。
package computer;

interface Pluggable {
   public void pluggedIn();
   public void pluggedOut();
}
现在我们将创建一个名为PenDrive的类来实现上述接口。
package computer;

public class PenDrive implements Pluggable {

   int storage = 64;

   public void pluggedIn () {
     System.out.println("Pen Drive is connected");
   }

   public void pluggedOut () {
     System.out.println("Pen Drive is removed");
   }

   public int storage() {
     return storage;
   }

   public static void main(String args[]) {
     PenDrive p = new PenDrive ();
     p.pluggedIn();
     System.out.println("Pen Drive Storage: " + p.storage());
     p.pluggedOut();
   }
}

如何在Java中创建包层次结构?

在形成层次结构时,Java 中的包以相反的顺序命名。这使得它们与目录或文件夹非常相似。就像在个人计算机上一样,一个文件夹可以包含一个或多个子文件夹,这同样适用于 Java 中的包。让我们看一个名为Asia.India.Kolkata的包。这些都是现有的文件夹,但如果考虑地理位置,很明显加尔各答位于印度,而印度位于亚洲。层次结构的主要目的是使类更容易查找。

Java 中的包类型

内置包

内置包是由大量内置类组成的包,这些内置类是 Java API 的一部分。这些套餐包括:
  • java.util - 该包包含有限数量的实用程序类,用于实现链表、集合等数据结构。它还支持日期和时间操作等等。
  • java.net - 它包含用于网络操作的类。

用户定义的包

用户定义的包称为用户包。用户可以手动创建一个包,并在其中包含任意数量的类。

如何从一个包访问另一个包?

您可以通过三种简单的方式从另一个包访问一个包:
  • 在导入语句中使用星号
在 Java 中,星号 ( * ) 字符用于表示“所有事物”。使用它,我们可以导入包的子包内的所有内容。 示例: 考虑一个名为tools的包。如果我们想导入这个包中的所有内容,那么我们需要使用 import 语句,例如:
import tools.*;
  • 使用 import package.ClassName;
在包中提及类名是仅将所需的类导入到程序中的有效方法,从而避免导入不必要的类。 示例: 考虑一个名为books的包。如果我们只想从中导入特定的类或接口(我们将查看Pages类),那么我们可以使用以下方法导入:
import book.Pages;
  • 使用您的全名
有一种方法可以通过使用 Java 包或其类的完全限定名称来直接使用它们。这样就不用导入包了,可以直接在程序中使用。 例子:
java.awt.List aSimpleList = new java.awt.List();

Java 中的默认批量大小

默认情况下,Java 导入java.lang包。它有许多简单 Java 程序中常用的类,例如StringInteger等。最重要的类之一是Object类,它也可以在java.lang包中找到。该包的大小取决于其组件:8 个接口、37 个类、3 个枚举、27 个异常、23 个错误类型和 5 个注释类型。

适合初学者的线程安全 Java 方法

来源:Medium 通过本文,您可以了解 Java 中线程安全方法的工作原理。 喝咖啡休息#165。 Java 中的包。 适合初学者的线程安全方法 - 2我发现许多初级/中级 Java 开发人员误解了线程安全方法在实际项目中应如何工作。而且由于我们通常工作在多线程环境中,因此它们的正确使用非常重要。线程安全方法是可以从多个线程同时调用而不影响彼此的数据状态的方法。对这个概念理解不足会导致难以发现和修复的错误。为了避免此类错误,让我们看一些例子。

示例#1:

public static int countLetters(String input) {
    int counter = 0;

    for (Character c : input.toCharArray()) {
        if (Character.isAlphabetic(c)) {
            counter++;
        }
    }

    return counter;
}
  • countLetters方法是静态的,它返回一个int值并接受一个字符串参数作为输入。
  • 该方法内部创建了一个原始变量计数器,然后循环遍历输入字符串的字符,并在每次遇到字母字符时递增变量计数器。
这个方法线程安全吗?许多开发人员说不,因为在这种情况下我们有一个非线程安全的增量操作。让我们弄清楚一下。在Java内存模型中,我们有栈和堆。每个线程都有自己的堆栈,并且所有线程共享同一个堆。在这方面,堆栈数据始终是线程安全的,但堆数据则不是。堆栈存储基元和对象引用。堆包含对象本身。这意味着在这个代码示例中,每个线程将自己的变量counter存储在堆栈上,并且不会对其他线程的数据产生任何影响,因此该方法是线程安全的请注意,输入String值也是一个对象,但String和基元包装器(IntegerLongDoubleBoolean等)是线程安全的,因为它们是不可变的。

示例#2:

public static int countLetters2(String input) {
    List<Character> listCounter = new ArrayList<>();

    for (Character c : input.toCharArray()) {
        if (Character.isAlphabetic(c)) {
            listCounter.add(c);
        }
    }

    return listCounter.size();
}
此代码使用与第一个示例相同的逻辑,但使用List对象而不是原始int变量。从前面的内容我们知道,Java中的对象是存储在堆中的,而List就是一个对象。我们还知道堆栈存储对堆上对象的引用。在示例 #2 中,每个线程创建一个新的ArrayList对象:并且listCounter变量在堆上存储对其对象的引用,因此其他线程无法更改该对象。
List<Character> listCounter = new ArrayList<>();
这意味着该方法是线程安全的。

示例#3:

public class CounterUtil { // singleton

    List<Character> listCounter = new ArrayList<>();

    public int countLetters3(String input) {
        for (Character c : input.toCharArray()) {
            if (Character.isAlphabetic(c)) {
                listCounter.add(c);
            }
        }

        return listCounter.size();
    }
}
在这个例子中,我们有一个单例(这很重要)类CounterUtil和一个全局变量listCounter。该变量与单例实例同时创建。当多个线程调用countChars3方法时,它们都使用同一个全局变量listCounter,该变量在堆上存储对同一对象的引用,并且那里的线程会相互影响。因此我们可以得出结论,该方法不是线程安全的。即使我们将List<Character> listCounter更改为原始变量int counter,它也不是线程安全的,因为所有线程都将使用相同的原始变量。

最后一个例子:

public static int countLetters4(List<Character> inputList) {
    List<Character> listCounter = new ArrayList<>();

    for (Character c : inputList) {
        if (Character.isAlphabetic(c)) {
            listCounter.add(c);
        }
    }

    return listCounter.size();
}
countLetters4 方法接受字符列表而不是字符串参数。这里我们不能保证这个方法是线程安全的。为什么?因为我们无法确定开发者将如何使用这个方法。如果外部的另一个线程与我们的counterLetters4方法同时更改inputList中的数据,则可能会影响最终结果。

结论

我们只看了四个示例,它们并没有涵盖 Java 项目中线程安全的所有方面,但它们是一个很好的起点。下次当您在代码中看到某个方法时,问问自己:“这个方法线程安全吗?” 很快你就会清楚地明白答案。
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION