Java 编程问题:七、Java 反射类、接口、构造器、方法和字段1

简介: Java 编程问题:七、Java 反射类、接口、构造器、方法和字段1

本章包括涉及 Java 反射 API 的 17 个问题。从经典主题,如检查和实例化 Java 工件(例如,模块、包、类、接口、超类、构造器、方法、注解和数组),到合成桥接构造或基于嵌套的访问控制(JDK11),本章详细介绍了 Java 反射 API。在本章结束时,Java 反射 API 将不会有任何秘密未被发现,您将准备好向您的同事展示反射可以做什么。

问题

使用以下问题来测试您的 Java 反射 API 编程能力。我强烈建议您在使用解决方案和下载示例程序之前,先尝试一下每个问题:

  1. 检查包:编写几个检查 Java 包的示例(例如名称、类列表等)。
  2. 检查类和超类:写几个检查类和超类的例子(例如,通过类名、修饰符、实现的接口、构造器、方法和字段获取Class)。
  3. 通过反射构造器来实例化:编写通过反射创建实例的程序。
  4. 获取接收器类型的注解:编写获取接收器类型注解的程序。
  5. 获得合成和桥接结构:编写一个程序,通过反射获得合成桥接结构。
  6. 检查变量个数:编写一个程序,检查一个方法是否获得变量个数。
  7. 检查默认方法:编写程序检查方法是否为default
  8. 基于嵌套的反射访问控制:编写一个程序,通过反射提供对基于嵌套的结构的访问。
  9. 获取器和设置器的反射:写几个例子,通过反射调用获取器和设置器。另外,编写一个程序,通过反射生成获取器和设置器。
  10. 反射注解:写几个通过反射获取不同种类注解的例子。
  11. 调用实例方法:编写一个程序,通过反射调用实例方法。
  12. 获取static方法:编写一个程序,对给定类的static方法进行分组,并通过反射调用其中一个方法。
  13. 获取方法、字段和异常的泛型类型:编写一个程序,通过反射获取给定方法、字段和异常的泛型类型。
  14. 获取公共和私有字段:编写一个程序,通过反射获取给定类的publicprivate字段。
  15. 使用数组:写几个通过反射使用数组的例子。
  16. 检查模块:写几个通过反射检查 Java9 模块的例子。
  17. 动态代理:编写依赖动态代理的程序,统计给定接口的方法调用次数。

解决方案

以下各节介绍上述问题的解决方案。记住,通常没有一个正确的方法来解决一个特定的问题。另外,请记住,这里显示的解释只包括解决问题所需的最有趣和最重要的细节。您可以从这个页面下载示例解决方案以查看更多详细信息并尝试程序。

149 检查包

当我们需要获取有关特定包的信息时,java.lang.Package类是我们的主要关注点。使用这个类,我们可以找到包的名称、实现这个包的供应商、它的标题、包的版本等等。

此类通常用于查找包含特定类的包的名称。例如,Integer类的包名可以容易地获得如下:

Class clazz = Class.forName("java.lang.Integer");
Package packageOfClazz = clazz.getPackage();
// java.lang
String packageNameOfClazz = packageOfClazz.getName();

现在,我们来查找File类的包名:

File file = new File(".");
Package packageOfFile = file.getClass().getPackage();
// java.io
String packageNameOfFile = packageOfFile.getName();

如果我们试图找到当前类的包名,那么我们可以依赖于this.getClass().getPackage().getName()。这在非静态环境中工作。

但是如果我们只想快速列出当前类装入器的所有包,那么我们可以依赖getPackages()方法,如下所示:

Package[] packages = Package.getPackages();

基于getPackages()方法,我们可以列出调用者的类装入器定义的所有包,以及以给定前缀开头的祖先包,如下所示:

public static List<String> fetchPackagesByPrefix(String prefix) {
  return Arrays.stream(Package.getPackages())
    .map(Package::getName)
    .filter(n -> n.startsWith(prefix))
    .collect(Collectors.toList());
}

如果这个方法存在于一个名为Packages的实用类中,那么我们可以如下调用它:

List<String> packagesSamePrefix 
  = Packages.fetchPackagesByPrefix("java.util");

您将看到类似于以下内容的输出:

java.util.function, java.util.jar, java.util.concurrent.locks,
java.util.spi, java.util.logging, ...

有时,我们只想在系统类加载器中列出一个包的所有类。让我们看看怎么做。

获取包的类

例如,我们可能希望列出当前应用的一个包中的类(例如,modern.challenge包)或编译时库中的一个包中的类(例如,commons-lang-2.4.jar

类被包装在可以在 Jar 中存档的包中,尽管它们不必这样。为了涵盖这两种情况,我们需要发现给定的包是否存在于 JAR 中。我们可以通过ClassLoader.getSystemClassLoader().getResource(package_path)加载资源并检查返回的资源 URL 来完成。如果包不在 JAR 中,那么资源将是以file:方案开始的 URL,如下面的示例(我们使用的是modern.challenge):

file:/D:/Java%20Modern%20Challenge/Code/Chapter%207/Inspect%20packages/build/classes/modern/challenge

但是如果包在 JAR 中(例如,org.apache.commons.lang3.builder,那么 URL 将以jar:方案开始,如下例所示:

jar:file:/D:/.../commons-lang3-3.9.jar!/org/apache/commons/lang3/builder

如果我们考虑到来自 JAR 的包的资源以jar:前缀开头,那么我们可以编写一个方法来区分它们,如下所示:

private static final String JAR_PREFIX = "jar:";
public static List<Class<?>> fetchClassesFromPackage(
    String packageName) throws URISyntaxException, IOException {
  List<Class<?>> classes = new ArrayList<>();
  String packagePath = packageName.replace('.', '/');
  URL resource = ClassLoader
    .getSystemClassLoader().getResource(packagePath);
  if (resource != null) {
    if (resource.toString().startsWith(JAR_PREFIX)) {
      classes.addAll(fetchClassesFromJar(resource, packageName));
    } else {
      File file = new File(resource.toURI());
      classes.addAll(fetchClassesFromDirectory(file, packageName));
    }
  } else {
    throw new RuntimeException("Resource not found for package: " 
      + packageName);
  }
  return classes;
}

因此,如果给定的包在 JAR 中,那么我们调用另一个辅助方法fetchClassesFromJar();否则,我们调用这个辅助方法fetchClassesFromDirectory()。顾名思义,这些助手知道如何从 JAR 或目录中提取给定包的类。

主要来说,这两种方法只是一些用来识别具有.class扩展名的文件的意大利面代码片段。每个类都通过Class.forName()来确保返回的是Class,而不是String。这两种方法在本书附带的代码中都可用。

如何列出不在系统类加载器中的包中的类,例如,外部 JAR 中的包?实现这一点的便捷方法依赖于URLClassLoader。此类用于从引用 JAR 文件和目录的 URL 搜索路径加载类和资源。我们将只处理 Jar,但对目录也这样做非常简单。

因此,根据给定的路径,我们需要获取所有 Jar 并将它们返回为URL[](这个数组需要定义URLClassLoader。例如,我们可以依赖于Files.find()方法遍历给定的路径并提取所有 Jar,如下所示:

public static URL[] fetchJarsUrlsFromClasspath(Path classpath)
    throws IOException {
  List<URL> urlsOfJars = new ArrayList<>();
  List<File> jarFiles = Files.find(
      classpath,
      Integer.MAX_VALUE,
      (path, attr) -> !attr.isDirectory() &&
        path.toString().toLowerCase().endsWith(JAR_EXTENSION))
      .map(Path::toFile)
      .collect(Collectors.toList());
  for (File jarFile: jarFiles) {
    try {
      urlsOfJars.add(jarFile.toURI().toURL());
    } catch (MalformedURLException e) {
      logger.log(Level.SEVERE, "Bad URL for{0} {1}",
        new Object[] {
          jarFile, e
        });
    }
  }
  return urlsOfJars.toArray(URL[]::new);
}

注意,我们正在扫描所有子目录,从给定的路径开始。当然,这是一个设计决策,很容易参数化搜索深度。现在,让我们从tomcat8/lib文件夹中获取 Jar(不需要为此安装 Tomcat;只需使用 Jar 的任何其他本地目录并进行适当的修改):

URL[] urls = Packages.fetchJarsUrlsFromClasspath(
  Path.of("D:/tomcat8/lib"));

现在,我们可以实例化URLClassLoader

URLClassLoader urlClassLoader = new URLClassLoader(
  urls, Thread.currentThread().getContextClassLoader());

这将为给定的 URL 构造一个新的URLClassLoader对象,并使用当前的类加载器进行委托(第二个参数也可以是null)。我们的URL[]只指向 JAR,但根据经验,假设任何jar:方案 URL 都引用 JAR 文件,而任何以/结尾的file:方案 URL 都引用目录。

tomcat8/lib文件夹中的一个 Jar 称为tomcat-jdbc.jar。在这个 JAR 中,有一个名为org.apache.tomcat.jdbc.pool的包。让我们列出这个包的类:

List<Class<?>> classes = Packages.fetchClassesFromPackage(
  "org.apache.tomcat.jdbc.pool", urlClassLoader);

fetchClassesFromPackage()方法是一个助手,它只扫描URLClassLoaderURL[]数组并获取给定包中的类。它的源代码与本书附带的代码一起提供。

检查模块内的包

如果我们使用 Java9 模块化,那么我们的包将生活在模块中。例如,如果我们在一个名为org.tournament的模块中的一个名为com.management的包中有一个名为Manager的类,那么我们可以这样获取该模块的所有包:

Manager mgt = new Manager();
Set<String> packages = mgt.getClass().getModule().getPackages();

另外,如果我们想创建一个类,那么我们需要以下的Class.forName()风格:

Class<?> clazz = Class.forName(mgt.getClass()
  .getModule(), "com.management.Manager");

请记住,每个模块在磁盘上都表示为具有相同名称的目录。例如,org.tournament模块在磁盘上有一个同名文件夹。此外,每个模块被映射为一个具有此名称的单独 JAR(例如,org.tournament.jar)。通过记住这些坐标,很容易修改本节中的代码,从而列出给定模块的给定包的所有类。

150 检查类

通过使用 Java 反射 API,我们可以检查类的详细信息,对象的类名、修饰符、构造器、方法、字段、实现接口等。

假设我们有以下Pair类:

public final class Pair<L, R> extends Tuple implements Comparable {
  final L left;
  final R right;
  public Pair(L left, R right) {
    this.left = left;
    this.right = right;
  }
  public class Entry<L, R> {}
    ...
}

我们还假设有一个实例:

Pair pair = new Pair(1, 1);

现在,让我们使用反射来获取Pair类的名称。

通过实例获取Pair类的名称

通过拥有Pair的实例(对象),我们可以通过调用getClass()方法,以及Class.getName()getSimpleName()getCanonicalName()找到其类的名称,如下例所示:

Class<?> clazz = pair.getClass();
// modern.challenge.Pair
System.out.println("Name: " + clazz.getName());
// Pair
System.out.println("Simple name: " + clazz.getSimpleName());
// modern.challenge.Pair
System.out.println("Canonical name: " + clazz.getCanonicalName());

匿名类没有简单的和规范的名称。

注意,getSimpleName()返回非限定类名。或者,我们可以获得如下类:

Class<Pair> clazz = Pair.class;
Class<?> clazz = Class.forName("modern.challenge.Pair");

获取Pair类修饰符

为了得到类的修饰符(publicprotectedprivatefinalstaticabstractinterface,我们可以调用Class.getModifiers()方法。此方法返回一个int值,该值将每个修饰符表示为标志位。为了解码结果,我们依赖于Modifier类,如下所示:

int modifiers = clazz.getModifiers();
System.out.println("Is public? " 
  + Modifier.isPublic(modifiers)); // true
System.out.println("Is final? " 
  + Modifier.isFinal(modifiers)); // true
System.out.println("Is abstract? " 
  + Modifier.isAbstract(modifiers)); // false

获取Pair类实现的接口

为了获得由类或对象表示的接口直接实现的接口,我们只需调用Class.getInterfaces()。此方法返回一个数组。因为Pair类实现了一个接口(Comparable,所以返回的数组将包含一个元素:

Class<?>[] interfaces = clazz.getInterfaces();
// interface java.lang.Comparable
System.out.println("Interfaces: " + Arrays.toString(interfaces));
// Comparable
System.out.println("Interface simple name: " 
  + interfaces[0].getSimpleName());

获取Pair类构造器

类的public构造器可以通过Class.getConstructors()类获得。返回结果为Constructor[]

Constructor<?>[] constructors = clazz.getConstructors();
// public modern.challenge.Pair(java.lang.Object,java.lang.Object)
System.out.println("Constructors: " + Arrays.toString(constructors));

要获取所有声明的构造器(例如,privateprotected构造器),请调用getDeclaredConstructors()。搜索某个构造器时,调用getConstructor(Class... parameterTypes)getDeclaredConstructor(Class... parameterTypes)

获取Pair类字段

类的所有字段都可以通过Class.getDeclaredFields()方法访问。此方法返回一个数组Field

Field[] fields = clazz.getDeclaredFields();
// final java.lang.Object modern.challenge.Pair.left
// final java.lang.Object modern.challenge.Pair.right
System.out.println("Fields: " + Arrays.toString(fields));

为了获取字段的实际名称,我们可以很容易地提供一个辅助方法:

public static List<String> getFieldNames(Field[] fields) {
  return Arrays.stream(fields)
    .map(Field::getName)
    .collect(Collectors.toList());
}

现在,我们只收到字段的名称:

List<String> fieldsName = getFieldNames(fields);
// left, right
System.out.println("Fields names: " + fieldsName);

获取字段的值可以通过一个名为Object get(Object obj)的通用方法和一组getFoo()方法来完成(有关详细信息,请参阅文档)。obj表示static或实例字段。例如,假设ProcedureOutputs类有一个名为callableStatementprivate字段,其类型为CallableStatement。让我们用Field.get()方法访问此字段,检查CallableStatement是否关闭:

ProcedureOutputs procedureOutputs 
  = storedProcedure.unwrap(ProcedureOutputs.class);
Field csField = procedureOutputs.getClass()
  .getDeclaredField("callableStatement"); 
csField.setAccessible(true);
CallableStatement cs 
  = (CallableStatement) csField.get(procedureOutputs);
System.out.println("Is closed? " + cs.isClosed());

如果只获取public字段,请调用getFields()。要搜索某个字段,请调用getField(String fieldName)getDeclaredField(String name)

获取Pair类方法

类的public方法可以通过Class.getMethods()方法访问。此方法返回一个数组Method

Method[] methods = clazz.getMethods();
// public boolean modern.challenge.Pair.equals(java.lang.Object)
// public int modern.challenge.Pair.hashCode()
// public int modern.challenge.Pair.compareTo(java.lang.Object)
// ...
System.out.println("Methods: " + Arrays.toString(methods));

为了获取方法的实际名称,我们可以快速提供一个辅助方法:

public static List<String> getMethodNames(Method[] methods) {
  return Arrays.stream(methods)
    .map(Method::getName)
    .collect(Collectors.toList());
}

现在,我们只检索方法的名称:

List<String> methodsName = getMethodNames(methods);
// equals, hashCode, compareTo, wait, wait,
// wait, toString, getClass, notify, notifyAll
System.out.println("Methods names: " + methodsName);

获取所有声明的方法(例如,privateprotected),调用getDeclaredMethods()。要搜索某个方法,请调用getMethod(String name, Class... parameterTypes)getDeclaredMethod(String name, Class... parameterTypes)

获取Pair类模块

如果我们使用 JDK9 模块化,那么我们的类将生活在模块中。Pair类不在模块中,但是我们可以通过 JDK9 的Class.getModule()方法很容易得到类的模块(如果类不在模块中,那么这个方法返回null):

// null, since Pair is not in a Module
Module module = clazz.getModule();

获取Pair类超类

Pair类扩展了Tuple类,因此Tuple类是Pair的超类。我们可以通过Class.getSuperclass()方法得到,如下所示:

Class<?> superClass = clazz.getSuperclass();
// modern.challenge.Tuple
System.out.println("Superclass: " + superClass.getName());

获取某个类型的名称

从 JDK8 开始,我们可以获得特定类型名称的信息字符串。

此方法返回与getName()getSimpleName()getCanonicalName()中的一个或多个相同的字符串:

  • 对于原始类型,它会为所有三个方法返回相同的结果:
System.out.println("Type: " + int.class.getTypeName()); // int
  • 对于Pair,返回与getName()getCanonicalName()相同的东西:
// modern.challenge.Pair
System.out.println("Type name: " + clazz.getTypeName());
  • 对于内部类(比如Entry代表Pair,它返回与getName()相同的东西:
// modern.challenge.Pair$Entry
System.out.println("Type name: " 
  + Pair.Entry.class.getTypeName());
  • 对于匿名类,它返回与getName()相同的内容:
Thread thread = new Thread() {
  public void run() {
    System.out.println("Child Thread");
  }
};
// modern.challenge.Main$1
System.out.println("Anonymous class type name: "
  + thread.getClass().getTypeName());
  • 对于数组,它返回与getCanonicalName()相同的内容:
Pair[] pairs = new Pair[10];
// modern.challenge.Pair[]
System.out.println("Array type name: " 
  + pairs.getClass().getTypeName());

获取描述类的字符串

从 JDK8 开始,我们可以通过Class.toGenericString()方法获得类的快速描述(包含修饰符、名称、类型参数等)。

我们来看几个例子:

// public final class modern.challenge.Pair<L,R>
System.out.println("Description of Pair: " 
  + clazz.toGenericString());
// public abstract interface java.lang.Runnable
System.out.println("Description of Runnable: " 
  + Runnable.class.toGenericString());
// public abstract interface java.util.Map<K,V>
System.out.println("Description of Map: " 
  + Map.class.toGenericString());

获取类的类型描述符字符串

从 JDK12 开始,我们可以通过Class.descriptorString()方法获取类的类型描述符作为String对象:

// Lmodern/challenge/Pair;
System.out.println("Type descriptor of Pair: " 
  + clazz.descriptorString());
// Ljava/lang/String;
System.out.println("Type descriptor of String: " 
  + String.class.descriptorString());

获取数组的组件类型

JDK12 只为数组提供了Class componentType()方法。此方法返回数组的组件类型,如下两个示例所示:

Pair[] pairs = new Pair[10];
String[] strings = new String[] {"1", "2", "3"};
// class modern.challenge.Pair
System.out.println("Component type of Pair[]: " 
  + pairs.getClass().componentType());
// class java.lang.String
System.out.println("Component type of String[]: " 
  + strings.getClass().componentType());

为数组类型获取类,其组件类型由Pair描述

从 JDK12 开始,我们可以得到一个数组类型的Class,该数组类型的组件类型由给定的类通过Class.arrayType()来描述:

Class<?> arrayClazz = clazz.arrayType();
// modern.challenge.Pair<L,R>[]
System.out.println("Array type: " + arrayClazz.toGenericString());

151 通过反射构造器的实例化

我们可以使用 Java 反射 API 通过Constructor.newInstance()实例化一个类。

让我们考虑以下类,它有四个构造器:

public class Car {
  private int id;
  private String name;
  private Color color;
  public Car() {}
  public Car(int id, String name) {
    this.id = id;
    this.name = name;
  }
  public Car(int id, Color color) {
    this.id = id;
    this.color = color;
  }
  public Car(int id, String name, Color color) {
    this.id = id;
    this.name = name;
    this.color = color;
  }
  // getters and setters omitted for brevity
}

一个Car实例可以通过这四个构造器中的一个来创建。Constructor类公开了一个方法,该方法接受构造器的参数类型,并返回反映匹配构造器的Constructor对象。这种方法称为getConstructor(Class... parameterTypes)

让我们调用前面的每个构造器:

Class<Car> clazz = Car.class;
Constructor<Car> emptyCnstr 
  = clazz.getConstructor();
Constructor<Car> idNameCnstr 
  = clazz.getConstructor(int.class, String.class);
Constructor<Car> idColorCnstr 
  = clazz.getConstructor(int.class, Color.class);
Constructor<Car> idNameColorCnstr 
  = clazz.getConstructor(int.class, String.class, Color.class);

此外,Constructor.newInstance(Object... initargs)可以返回Car的实例,该实例对应于被调用的构造器:

Car carViaEmptyCnstr = emptyCnstr.newInstance();
Car carViaIdNameCnstr = idNameCnstr.newInstance(1, "Dacia");
Car carViaIdColorCnstr = idColorCnstr
  .newInstance(1, new Color(0, 0, 0));
Car carViaIdNameColorCnstr = idNameColorCnstr
  .newInstance(1, "Dacia", new Color(0, 0, 0));

现在,我们来看看如何通过反射实例化一个private构造器。

通过私有构造器实例化类

Java 反射 API 也可以通过其private构造器来实例化类。例如,假设我们有一个名为Cars的工具类。按照最佳实践,我们将此类定义为final,并使用private构造器来禁止实例:

public final class Cars {
  private Cars() {}
    // static members
}

取这个构造器可以通过Class.getDeclaredConstructor()完成,如下:

Class<Cars> carsClass = Cars.class;
Constructor<Cars> emptyCarsCnstr = carsClass.getDeclaredConstructor();

在这个实例中调用newInstance()会抛出IllegalAccessException,因为被调用的构造器有private访问权限。但是,Java 反射允许我们通过标志方法Constructor.setAccessible()修改访问级别。这一次,实例化按预期工作:

emptyCarsCnstr.setAccessible(true);
Cars carsViaEmptyCnstr = emptyCarsCnstr.newInstance();

为了阻止这种方法,建议抛出一个来自private构造器的错误,如下所示:

public final class Cars {
  private Cars() {
    throw new AssertionError("Cannot be instantiated");
  }
  // static members
}

这一次,实例化尝试将以AssertionError失败。

Java 编程问题:七、Java 反射类、接口、构造器、方法和字段2https://developer.aliyun.com/article/1426146

相关文章
|
18天前
|
JSON Java Apache
Java基础-常用API-Object类
继承是面向对象编程的重要特性,允许从已有类派生新类。Java采用单继承机制,默认所有类继承自Object类。Object类提供了多个常用方法,如`clone()`用于复制对象,`equals()`判断对象是否相等,`hashCode()`计算哈希码,`toString()`返回对象的字符串表示,`wait()`、`notify()`和`notifyAll()`用于线程同步,`finalize()`在对象被垃圾回收时调用。掌握这些方法有助于更好地理解和使用Java中的对象行为。
|
21天前
|
数据采集 JSON Java
利用Java获取京东SKU接口指南
本文介绍如何使用Java通过京东API获取商品SKU信息。首先,需注册京东开放平台账号并创建应用以获取AppKey和AppSecret。接着,查阅API文档了解调用方法。明确商品ID后,构建请求参数并通过HTTP客户端发送请求。最后,解析返回的JSON数据提取SKU信息。注意遵守API调用频率限制及数据保护法规。此方法适用于电商平台及其他数据获取场景。
|
26天前
|
安全 Java API
java如何请求接口然后终止某个线程
通过本文的介绍,您应该能够理解如何在Java中请求接口并根据返回结果终止某个线程。合理使用标志位或 `interrupt`方法可以确保线程的安全终止,而处理好网络请求中的各种异常情况,可以提高程序的稳定性和可靠性。
48 6
|
2月前
|
存储 缓存 安全
java 中操作字符串都有哪些类,它们之间有什么区别
Java中操作字符串的类主要有String、StringBuilder和StringBuffer。String是不可变的,每次操作都会生成新对象;StringBuilder和StringBuffer都是可变的,但StringBuilder是非线程安全的,而StringBuffer是线程安全的,因此性能略低。
69 8
|
2月前
|
Java API
Java中内置的函数式接口
Java中内置的函数式接口
35 2
|
2月前
|
Java
在Java中,接口之间可以继承吗?
接口继承是一种重要的机制,它允许一个接口从另一个或多个接口继承方法和常量。
140 1
|
5天前
|
监控 Java
java异步判断线程池所有任务是否执行完
通过上述步骤,您可以在Java中实现异步判断线程池所有任务是否执行完毕。这种方法使用了 `CompletionService`来监控任务的完成情况,并通过一个独立线程异步检查所有任务的执行状态。这种设计不仅简洁高效,还能确保在大量任务处理时程序的稳定性和可维护性。希望本文能为您的开发工作提供实用的指导和帮助。
44 17
|
16天前
|
Java
Java—多线程实现生产消费者
本文介绍了多线程实现生产消费者模式的三个版本。Version1包含四个类:`Producer`(生产者)、`Consumer`(消费者)、`Resource`(公共资源)和`TestMain`(测试类)。通过`synchronized`和`wait/notify`机制控制线程同步,但存在多个生产者或消费者时可能出现多次生产和消费的问题。 Version2将`if`改为`while`,解决了多次生产和消费的问题,但仍可能因`notify()`随机唤醒线程而导致死锁。因此,引入了`notifyAll()`来唤醒所有等待线程,但这会带来性能问题。
Java—多线程实现生产消费者
|
1天前
|
缓存 安全 算法
Java 多线程 面试题
Java 多线程 相关基础面试题
|
18天前
|
安全 Java Kotlin
Java多线程——synchronized、volatile 保障可见性
Java多线程中,`synchronized` 和 `volatile` 关键字用于保障可见性。`synchronized` 保证原子性、可见性和有序性,通过锁机制确保线程安全;`volatile` 仅保证可见性和有序性,不保证原子性。代码示例展示了如何使用 `synchronized` 和 `volatile` 解决主线程无法感知子线程修改共享变量的问题。总结:`volatile` 确保不同线程对共享变量操作的可见性,使一个线程修改后,其他线程能立即看到最新值。