code: @Override
    public void copy(
        String srcBucketName, List<String> srcObjectNames,
        String dstBucketName, List<String> dstObjectNames)
        throws IOException {
      validateCopyArguments(srcBucketName, srcObjectNames, dstBucketName, dstObjectNames, this);
  
      if (srcObjectNames.isEmpty()) {
        return;
      }
  
      // Gather FileNotFoundExceptions for individual objects,
      // but only throw a single combined exception at the end.
      KeySetView<IOException, Boolean> innerExceptions = ConcurrentHashMap.newKeySet();
  
      // Perform the copy operations.
      BatchHelper batchHelper =
          batchFactory.newBatchHelper(
              httpRequestInitializer,
              gcs,
              storageOptions.getCopyMaxRequestsPerBatch(),
              srcObjectNames.size(),
              storageOptions.getCopyBatchThreads());
  
      for (int i = 0; i < srcObjectNames.size(); i++) {
        if (storageOptions.isCopyWithRewriteEnabled()) {
          // Rewrite request has the same effect as Copy, but it can handle moving
          // large objects that may potentially timeout a Copy request.
          rewriteInternal(
              batchHelper,
              innerExceptions,
              srcBucketName, srcObjectNames.get(i),
              dstBucketName, dstObjectNames.get(i));
        } else {
          copyInternal(
              batchHelper,
              innerExceptions,
              srcBucketName, srcObjectNames.get(i),
              dstBucketName, dstObjectNames.get(i));
        }
      }
  
      // Execute any remaining requests not divisible by the max batch size.
      batchHelper.flush();
  
      if (!innerExceptions.isEmpty()) {
        throw GoogleCloudStorageExceptions.createCompositeException(innerExceptions);
      }
    }
docs: See {@link GoogleCloudStorage#copy(String, List, String, List)} for details about expected
  behavior.