二十、泛型(2)

news2024/11/25 11:29:30

本章概要

  • 泛型接口
  • 泛型方法
    • 变长参数和泛型方法
    • 一个泛型的 Supplier
    • 简化元组的使用
    • 一个 Set 工具

泛型接口

泛型也可以应用于接口。例如 生成器,这是一种专门负责创建对象的类。实际上,这是 工厂方法 设计模式的一种应用。不过,当使用生成器创建新的对象时,它不需要任何参数,而工厂方法一般需要参数。生成器无需额外的信息就知道如何创建新对象。

一般而言,一个生成器只定义一个方法,用于创建对象。例如 java.util.function 类库中的 Supplier 就是一个生成器,调用其 get() 获取对象。get() 是泛型方法,返回值为类型参数 T

为了演示 Supplier,我们需要定义几个类。下面是个咖啡相关的继承体系:

Coffee.java

public class Coffee {
    private static long counter = 0;
    private final long id = counter++;

    @Override
    public String toString() {
        return getClass().getSimpleName() + " " + id;
    }
}

Latte.java

public class Latte extends Coffee {
}

Mocha.java

public class Mocha extends Coffee {
}

Cappuccino.java

public class Cappuccino extends Coffee {
}

Americano.java

public class Americano extends Coffee {
}

Breve.java

public class Breve extends Coffee {
}

现在,我们可以编写一个类,实现 Supplier<Coffee> 接口,它能够随机生成不同类型的 Coffee 对象:

import java.util.*;
import java.util.function.*;
import java.util.stream.*;

public class CoffeeSupplier
        implements Supplier<Coffee>, Iterable<Coffee> {
    private Class<?>[] types = {Latte.class, Mocha.class,
            Cappuccino.class, Americano.class, Breve.class};
    private static Random rand = new Random(47);

    public CoffeeSupplier() {
    }

    // For iteration:
    private int size = 0;

    public CoffeeSupplier(int sz) {
        size = sz;
    }

    @Override
    public Coffee get() {
        try {
            return (Coffee) types[rand.nextInt(types.length)].newInstance();
        } catch (InstantiationException | IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }

    class CoffeeIterator implements Iterator<Coffee> {
        int count = size;

        @Override
        public boolean hasNext() {
            return count > 0;
        }

        @Override
        public Coffee next() {
            count--;
            return CoffeeSupplier.this.get();
        }

        @Override
        public void remove() {
            throw new UnsupportedOperationException();
        }
    }

    @Override
    public Iterator<Coffee> iterator() {
        return new CoffeeIterator();
    }

    public static void main(String[] args) {
        Stream.generate(new CoffeeSupplier())
                .limit(5)
                .forEach(System.out::println);
        for (Coffee c : new CoffeeSupplier(5)) {
            System.out.println(c);
        }
    }
}

输出结果:

在这里插入图片描述

参数化的 Supplier 接口确保 get() 返回值是参数的类型。CoffeeSupplier 同时还实现了 Iterable 接口,所以能用于 for-in 语句。不过,它还需要知道何时终止循环,这正是第二个构造函数的作用。

下面是另一个实现 Supplier<T> 接口的例子,它负责生成 Fibonacci 数列:

import java.util.function.*;
import java.util.stream.*;

public class Fibonacci implements Supplier<Integer> {
    private int count = 0;

    @Override
    public Integer get() {
        return fib(count++);
    }

    private int fib(int n) {
        if (n < 2) {
            return 1;
        }
        return fib(n - 2) + fib(n - 1);
    }

    public static void main(String[] args) {
        Stream.generate(new Fibonacci())
                .limit(18)
                .map(n -> n + " ")
                .forEach(System.out::print);
    }
}

输出结果:

在这里插入图片描述

虽然我们在 Fibonacci 类的里里外外使用的都是 int 类型,但是其参数类型却是 Integer。这个例子引出了 Java 泛型的一个局限性:基本类型无法作为类型参数。不过 Java 5 具备自动装箱和拆箱的功能,可以很方便地在基本类型和相应的包装类之间进行转换。通过这个例子中 Fibonacci 类对 int 的使用,我们已经看到了这种效果。

如果还想更进一步,编写一个实现了 IterableFibnoacci 生成器。我们的一个选择是重写这个类,令其实现 Iterable 接口。不过,你并不是总能拥有源代码的控制权,并且,除非必须这么做,否则,我们也不愿意重写一个类。而且我们还有另一种选择,就是创建一个 适配器 (Adapter) 来实现所需的接口,我们在前面介绍过这个设计模式。

有多种方法可以实现适配器。例如,可以通过继承来创建适配器类:

import java.util.*;

public class IterableFibonacci
        extends Fibonacci implements Iterable<Integer> {
    private int n;

    public IterableFibonacci(int count) {
        n = count;
    }

    @Override
    public Iterator<Integer> iterator() {
        return new Iterator<Integer>() {
            @Override
            public boolean hasNext() {
                return n > 0;
            }

            @Override
            public Integer next() {
                n--;
                return IterableFibonacci.this.get();
            }

            @Override
            public void remove() { // Not implemented
                throw new UnsupportedOperationException();
            }
        };
    }

    public static void main(String[] args) {
        for (int i : new IterableFibonacci(18)) {
            System.out.print(i + " ");
        }
    }
}

输出结果:

在这里插入图片描述

for-in 语句中使用 IterableFibonacci,必须在构造函数中提供一个边界值,这样 hasNext() 才知道何时返回 false,结束循环。

泛型方法

到目前为止,我们已经研究了参数化整个类。其实还可以参数化类中的方法。类本身可能是泛型的,也可能不是,不过这与它的方法是否是泛型的并没有什么关系。

泛型方法独立于类而改变方法。作为准则,请“尽可能”使用泛型方法。通常将单个方法泛型化要比将整个类泛型化更清晰易懂。

如果方法是 static 的,则无法访问该类的泛型类型参数,因此,如果使用了泛型类型参数,则它必须是泛型方法。

要定义泛型方法,请将泛型参数列表放置在返回值之前,如下所示:

public class GenericMethods {
    public <T> void f(T x) {
        System.out.println(x.getClass().getName());
    }

    public static void main(String[] args) {
        GenericMethods gm = new GenericMethods();
        gm.f("");
        gm.f(1);
        gm.f(1.0);
        gm.f(1.0F);
        gm.f('c');
        gm.f(gm);
    }
}

在这里插入图片描述

尽管可以同时对类及其方法进行参数化,但这里未将 GenericMethods 类参数化。只有方法 f() 具有类型参数,该参数由方法返回类型之前的参数列表指示。

对于泛型类,必须在实例化该类时指定类型参数。使用泛型方法时,通常不需要指定参数类型,因为编译器会找出这些类型。 这称为 类型参数推断。因此,对 f() 的调用看起来像普通的方法调用,并且 f() 看起来像被重载了无数次一样。它甚至会接受 GenericMethods 类型的参数。

如果使用基本类型调用 f() ,自动装箱就开始起作用,自动将基本类型包装在它们对应的包装类型中。

变长参数和泛型方法

泛型方法和变长参数列表可以很好地共存:

import java.util.ArrayList;
import java.util.List;

public class GenericVarargs {
    @SafeVarargs
    public static <T> List<T> makeList(T... args) {
        List<T> result = new ArrayList<>();
        for (T item : args)
            result.add(item);
        return result;
    }

    public static void main(String[] args) {
        List<String> ls = makeList("A");
        System.out.println(ls);
        ls = makeList("A", "B", "C");
        System.out.println(ls);
        ls = makeList(
                "ABCDEFFHIJKLMNOPQRSTUVWXYZ".split(""));
        System.out.println(ls);
    }
}

在这里插入图片描述

此处显示的 makeList() 方法产生的功能与标准库的 java.util.Arrays.asList() 方法相同。

@SafeVarargs 注解保证我们不会对变长参数列表进行任何修改,这是正确的,因为我们只从中读取。如果没有此注解,编译器将无法知道这些并会发出警告。

一个泛型的 Supplier

这是一个为任意具有无参构造方法的类生成 Supplier 的类。为了减少键入,它还包括一个用于生成 BasicSupplier 的泛型方法:

import java.util.function.Supplier;

public class BasicSupplier<T> implements Supplier<T> {
    private Class<T> type;

    public BasicSupplier(Class<T> type) {
        this.type = type;
    }

    @Override
    public T get() {
        try {
            // Assumes type is a public class:
            return type.newInstance();
        } catch (InstantiationException |
                 IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }

    // Produce a default Supplier from a type token:
    public static <T> Supplier<T> create(Class<T> type) {
        return new BasicSupplier<>(type);
    }
}

此类提供了产生以下对象的基本实现:

  1. public 的。 因为 BasicSupplier 在单独的包中,所以相关的类必须具有 public 权限,而不仅仅是包级访问权限。
  2. 具有无参构造方法。要创建一个这样的 BasicSupplier 对象,请调用 create() 方法,并将要生成类型的类型令牌传递给它。通用的 create() 方法提供了 BasicSupplier.create(MyType.class) 这种较简洁的语法来代替较笨拙的 new BasicSupplier <MyType>(MyType.class)

例如,这是一个具有无参构造方法的简单类:

public class CountedObject {
    private static long counter = 0;
    private final long id = counter++;

    public long id() {
        return id;
    }

    @Override
    public String toString() {
        return "CountedObject " + id;
    }
}

CountedObject 类可以跟踪自身创建了多少个实例,并通过 toString() 报告这些实例的数量。 BasicSupplier 可以轻松地为 CountedObject 创建 Supplier

import java.util.stream.Stream;

public class BasicSupplierDemo {
    public static void main(String[] args) {
        Stream.generate(
                        BasicSupplier.create(CountedObject.class))
                .limit(5)
                .forEach(System.out::println);
    }
}

在这里插入图片描述

泛型方法减少了产生 Supplier 对象所需的代码量。 Java 泛型强制传递 Class 对象,以便在 create() 方法中将其用于类型推断。

简化元组的使用

使用类型参数推断和静态导入,我们将把早期的元组重写为更通用的库。在这里,我们使用重载的静态方法创建元组:

Tuple.java

public class Tuple {
    public static <A, B> Tuple2<A, B> tuple(A a, B b) {
        return new Tuple2<>(a, b);
    }

    public static <A, B, C> Tuple3<A, B, C>
    tuple(A a, B b, C c) {
        return new Tuple3<>(a, b, c);
    }

    public static <A, B, C, D> Tuple4<A, B, C, D>
    tuple(A a, B b, C c, D d) {
        return new Tuple4<>(a, b, c, d);
    }

    public static <A, B, C, D, E>
    Tuple5<A, B, C, D, E> tuple(A a, B b, C c, D d, E e) {
        return new Tuple5<>(a, b, c, d, e);
    }
}

Tuple2.java

public class Tuple2<A, B> {
    public final A a1;
    public final B a2;

    public Tuple2(A a, B b) {
        a1 = a;
        a2 = b;
    }

    public String rep() {
        return a1 + ", " + a2;
    }

    @Override
    public String toString() {
        return "(" + rep() + ")";
    }
}

Tuple3.java

public class Tuple3<A, B, C> extends Tuple2<A, B> {
    public final C a3;

    public Tuple3(A a, B b, C c) {
        super(a, b);
        a3 = c;
    }

    @Override
    public String rep() {
        return super.rep() + ", " + a3;
    }
}

Tuple4.java

public class Tuple4<A, B, C, D>
        extends Tuple3<A, B, C> {
    public final D a4;

    public Tuple4(A a, B b, C c, D d) {
        super(a, b, c);
        a4 = d;
    }
    
    @Override
    public String rep() {
        return super.rep() + ", " + a4;
    }
}

Tuple5.java

public class Tuple5<A, B, C, D, E>
        extends Tuple4<A, B, C, D> {
    public final E a5;

    public Tuple5(A a, B b, C c, D d, E e) {
        super(a, b, c, d);
        a5 = e;
    }

    @Override
    public String rep() {
        return super.rep() + ", " + a5;
    }
}

我们修改 TupleTest.java 来测试 Tuple.java :

import static com.example.test.Tuple.tuple;

public class TupleTest2 {
    static Tuple2<String, Integer> f() {
        return tuple("hi", 47);
    }

    static Tuple2 f2() {
        return tuple("hi", 47);
    }

    static Tuple3<Amphibian, String, Integer> g() {
        return tuple(new Amphibian(), "hi", 47);
    }

    static Tuple4<Vehicle, Amphibian, String, Integer> h() {
        return tuple(
                new Vehicle(), new Amphibian(), "hi", 47);
    }

    static Tuple5<Vehicle, Amphibian,
            String, Integer, Double> k() {
        return tuple(new Vehicle(), new Amphibian(),
                "hi", 47, 11.1);
    }

    public static void main(String[] args) {
        Tuple2<String, Integer> ttsi = f();
        System.out.println(ttsi);
        System.out.println(f2());
        System.out.println(g());
        System.out.println(h());
        System.out.println(k());
    }
}

Americano.java

public class Americano extends Coffee {
}

Vehicle.java

public class Vehicle {
}

在这里插入图片描述

请注意,f() 返回一个参数化的 Tuple2 对象,而 f2() 返回一个未参数化的 Tuple2 对象。编译器不会在这里警告 f2() ,因为返回值未以参数化方式使用。从某种意义上说,它被“向上转型”为一个未参数化的 Tuple2 。 但是,如果尝试将 f2() 的结果放入到参数化的 Tuple2 中,则编译器将发出警告。

一个 Set 工具

对于泛型方法的另一个示例,请考虑由 Set 表示的数学关系。这些被方便地定义为可用于所有不同类型的泛型方法:

import java.util.HashSet;
import java.util.Set;

public class Sets {
    public static <T> Set<T> union(Set<T> a, Set<T> b) {
        Set<T> result = new HashSet<>(a);
        result.addAll(b);
        return result;
    }

    public static <T>
    Set<T> intersection(Set<T> a, Set<T> b) {
        Set<T> result = new HashSet<>(a);
        result.retainAll(b);
        return result;
    }

    // Subtract subset from superset:
    public static <T> Set<T>
    difference(Set<T> superset, Set<T> subset) {
        Set<T> result = new HashSet<>(superset);
        result.removeAll(subset);
        return result;
    }

    // Reflexive--everything not in the intersection:
    public static <T> Set<T> complement(Set<T> a, Set<T> b) {
        return difference(union(a, b), intersection(a, b));
    }
}

前三个方法通过将第一个参数的引用复制到新的 HashSet 对象中来复制第一个参数,因此不会直接修改参数集合。因此,返回值是一个新的 Set 对象。

这四种方法代表数学集合操作: union() 返回一个包含两个参数并集的 Setintersection() 返回一个包含两个参数集合交集的 Setdifference()superset 中减去 subset 的元素 ,而 complement() 返回所有不在交集中的元素的 Set。作为显示这些方法效果的简单示例的一部分,下面是一个包含不同水彩名称的 enum

public enum Watercolors {
    ZINC, LEMON_YELLOW, MEDIUM_YELLOW, DEEP_YELLOW,
    ORANGE, BRILLIANT_RED, CRIMSON, MAGENTA,
    ROSE_MADDER, VIOLET, CERULEAN_BLUE_HUE,
    PHTHALO_BLUE, ULTRAMARINE, COBALT_BLUE_HUE,
    PERMANENT_GREEN, VIRIDIAN_HUE, SAP_GREEN,
    YELLOW_OCHRE, BURNT_SIENNA, RAW_UMBER,
    BURNT_UMBER, PAYNES_GRAY, IVORY_BLACK
}

为了方便起见(不必全限定所有名称),将其静态导入到以下示例中。本示例使用 EnumSet 轻松从 enum 中创建 Set 。在这里,静态方法 EnumSet.range() 要求提供所要在结果 Set 中创建的元素范围的第一个和最后一个元素:

import java.util.EnumSet;
import java.util.Set;

import static com.example.test.Sets.*;
import static com.example.test.Watercolors.*;

public class WatercolorSets {
    public static void main(String[] args) {
        Set<Watercolors> set1 =
                EnumSet.range(BRILLIANT_RED, VIRIDIAN_HUE);
        Set<Watercolors> set2 =
                EnumSet.range(CERULEAN_BLUE_HUE, BURNT_UMBER);
        System.out.println("set1: " + set1);
        System.out.println("set2: " + set2);
        System.out.println(
                "union(set1, set2): " + union(set1, set2));
        Set<Watercolors> subset = intersection(set1, set2);
        System.out.println(
                "intersection(set1, set2): " + subset);
        System.out.println("difference(set1, subset): " +
                difference(set1, subset));
        System.out.println("difference(set2, subset): " +
                difference(set2, subset));
        System.out.println("complement(set1, set2): " +
                complement(set1, set2));
    }
}

在这里插入图片描述

接下来的例子使用 Sets.difference() 方法来展示 java.util 包中各种 CollectionMap 类之间的方法差异:

import java.lang.reflect.Method;
import java.util.*;
import java.util.stream.Collectors;

public class CollectionMethodDifferences {
    static Set<String> methodSet(Class<?> type) {
        return Arrays.stream(type.getMethods())
                .map(Method::getName)
                .collect(Collectors.toCollection(TreeSet::new));
    }

    static void interfaces(Class<?> type) {
        System.out.print("Interfaces in " +
                type.getSimpleName() + ": ");
        System.out.println(
                Arrays.stream(type.getInterfaces())
                        .map(Class::getSimpleName)
                        .collect(Collectors.toList()));
    }

    static Set<String> object = methodSet(Object.class);

    static {
        object.add("clone");
    }

    static void
    difference(Class<?> superset, Class<?> subset) {
        System.out.print(superset.getSimpleName() +
                " extends " + subset.getSimpleName() +
                ", adds: ");
        Set<String> comp = Sets.difference(
                methodSet(superset), methodSet(subset));
        comp.removeAll(object); // Ignore 'Object' methods
        System.out.println(comp);
        interfaces(superset);
    }

    public static void main(String[] args) {
        System.out.println("Collection: " +
                methodSet(Collection.class));
        interfaces(Collection.class);
        difference(Set.class, Collection.class);
        difference(HashSet.class, Set.class);
        difference(LinkedHashSet.class, HashSet.class);
        difference(TreeSet.class, Set.class);
        difference(List.class, Collection.class);
        difference(ArrayList.class, List.class);
        difference(LinkedList.class, List.class);
        difference(Queue.class, Collection.class);
        difference(PriorityQueue.class, Queue.class);
        System.out.println("Map: " + methodSet(Map.class));
        difference(HashMap.class, Map.class);
        difference(LinkedHashMap.class, HashMap.class);
        difference(SortedMap.class, Map.class);
        difference(TreeMap.class, Map.class);
    }
}

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1178092.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基础课23——设计客服机器人

根据调查数据显示&#xff0c;使用纯机器人完全替代客服的情况并不常见&#xff0c;人机结合模式的使用更为普遍。在这两种模式中&#xff0c;不满意用户的占比都非常低&#xff0c;不到1%。然而&#xff0c;在满意用户方面&#xff0c;人机结合模式的用户满意度明显高于其他模…

freertos简单串口

先来完善一下FreeRTOSConfig.h这个配置文件 /*FreeRTOS V9.0.0 - Copyright (C) 2016 Real Time Engineers Ltd.All rights reservedVISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION.This file is part of the FreeRTOS distribution.FreeRTOS is …

无代码平台哪家好,盘点最新国内十大无代码零代码平台排名

无代码&#xff08;No Code&#xff09;是一种通过使用可视化界面和预构建的模块来创建应用程序、网站或其他数字化解决方案的方法&#xff0c;不需要编写大量的手动代码。 无代码平台通常包括一些基本的构建块&#xff0c;如表单、按钮、文本框等&#xff0c;用户可以通过拖拽…

用小程序打造品牌网站的效果如何

网站是企业线上门户&#xff0c;很多企业或商家在成立之初就会选择搭建企业官网展示品牌、承载信息及服务相关预约咨询等&#xff0c;当然除了搭建网页外&#xff0c;还可以通过小程序实现官网效果。 除了浏览器流量外&#xff0c;如微信、百度、头条抖音、支付宝、快手等平台…

ACWing.第 128 场周赛 (B、C题解)

B、5286. 翻倍&#xff08;思维推导&#xff09; 一、题目要求 给定两个正整数&#xff0c;初始时两数均为 1。 你可以进行任意次&#xff08;也可以不进行&#xff09;翻倍操作&#xff0c;每次操作任选一个非负整数 k&#xff0c;令两数中的一个数乘以 k&#xff0c;另一个…

MySQL数据库之表操作

目录 表的操作1.创建表创建表案例 2.查看表结构3.修改表4.删除表 表的操作 1.创建表 语法&#xff1a; CREATE TABLE table_name (field1 datatype,field2 datatype,field3 datatype ) character set 字符集 collate 校验规则 engine 存储引擎;说明&#xff1a; field 表示列…

Python异步编程--获取girlypic写真集

前言 在日常生活中&#xff0c;无论是网络配置、文件整理、web开发还是工具&#xff0c;时常用到Python写些脚本。 这次主要是分享下异步编程的经验&#xff0c;就拿获取girlypic的图片举例吧&#xff0c;也希望能给一些同学带来思考。 使用argparse而不是os.args&#xff0…

pytorch直线拟合

目录 1、数据分析 2、pytorch直线拟合 1、数据分析 直线拟合的前提条件通常包括以下几点&#xff1a; 存在线性关系&#xff1a;这是进行直线拟合的基础&#xff0c;数据点之间应该存在一种线性关系&#xff0c;即数据的分布可以用直线来近似描述。这种线性关系可以是数据点…

86.Linux系统下复制进程fork(逻辑地址和物理地址)

目录 fork复制进程 逻辑地址和物理地址 fork复制进程 fork 是一个系统调用&#xff0c;在 Linux/Unix 系统中用于创建一个新的进程&#xff0c;新进程称为子进程。子进程是父进程的副本&#xff0c;它从父进程那里继承了大部分属性和资源&#xff0c;包括代码、数据、打开的文…

【逗老师的无线电】Debian Linux手工编译安装MMDVM

看我干了啥&#xff0c;在Vmware里面装了一个Debian Linux并且运行了MMDVMHost&#xff0c;来支持业余无线电通联 开始之前先举个手&#xff0c;有多少朋友能分清MMDVM和Pi-Star关系的&#xff1f; MMDVM、Pi-Star和树莓派的关系 咱们先科普一下这个小知识点。各位HAM们应…

Python基础(第五期): python数据容器(序列) 列表 集合 元素 字符串 字典 序列遍历操作

python基础专栏 python基础&#xff08;第五期&#xff09; 文章目录 python基础&#xff08;第五期&#xff09;数据容器一、列表1、列表的定义2、列表的下标索引 3、列表的(添加)方法3.1 列表的查询方法3.2 修改特定下标索引的值3.3 列表指定位置插入元素3.3 列表指定元素的追…

自动驾驶高效预训练--降低落地成本的新思路(AD-PT)

自动驾驶高效预训练--降低落地成本的新思路 1. 之前的方法2. 主要工作——面向自动驾驶的点云预训练2.1. 数据准备 出发点&#xff1a;通过预训练的方式&#xff0c;可以利用大量无标注数据进一步提升3D检测 https://arxiv.org/pdf/2306.00612.pdf 1. 之前的方法 1.基于对比学…

setTimeout和setImmediate以及process.nextTick的区别?

目录 前言 setTimeout 特性和用法 setImmediate 特性和用法 process.nextTick 特性和用法 区别和示例 总结 在Node.js中&#xff0c;setTimeout、setImmediate和process.nextTick是用于调度异步操作的三种不同机制。它们之间的区别在于事件循环中的执行顺序和优先级。…

django安装数据库

使用pip安装django pip3 install django注意我使用的是python3所以用pip3安装&#xff0c;如需安装指定版本 django ..* 检测是否安装成功,不报错&#xff0c;则安装成功 # python3 # import django下边这是报错的 django迁移数据库 再mysql中简历数据库 CREATE DATABA…

COOHOM通过采用亚马逊云科“专库专用”的方式,为云原生的构建提供稳定的数据支撑

全球化浪潮下&#xff0c;面对全球化业务发展带来的新需求与新挑战&#xff0c;越来越多的企业开启了云原生构建旅程&#xff0c;以推动业务系统快速迭代&#xff0c;为国际业务的拓展打下坚实的基础。COOHOM是杭州群核信息技术有限公司旗下的国际化品牌。为全球企业和个人提供…

au怎么把音乐和人声单独分离出来?分享最简单的方法!

把音乐和人声单独分离&#xff0c;对于音频处理和后期制作来说&#xff0c;可以方便地对人声或音乐进行单独的处理和编辑&#xff0c;以达到更好的效果。下面介绍了怎么把利用AU音乐和人声单独分离出来的详细步骤。 一、AU 1、把自己需要处理的那首歌copy到桌面&#xff0c;再…

npm install:sill idealTree buildDeps

执行npm install&#xff0c;卡在 sill idealTree buildDeps PS D:\workspace-groovy\attendance-india-web> npm install -g cnpm --registryhttps://registry.npm.taobao.org [..................] / idealTree:node_global: sill idealTree buildDeps[.................…

BUUCTF 隐藏的钥匙 1

BUUCTF:https://buuoj.cn/challenges 题目描述&#xff1a; 路飞一行人千辛万苦来到了伟大航道的终点&#xff0c;找到了传说中的One piece&#xff0c;但是需要钥匙才能打开One Piece大门&#xff0c;钥匙就隐藏在下面的图片中&#xff0c;聪明的你能帮路飞拿到钥匙&#xff…

视频编辑软件Corel VideoStudio 会声会影2024中文剪辑使用教程

会声会影&#xff08;Corel VideoStudio&#xff09;2024为加拿大Corel公司发布的一款功能丰富的视频编辑软件。会声会影2023简单易用&#xff0c;具有史无前例的强大功能&#xff0c;拖放式标题、转场、覆叠和滤镜&#xff0c;色彩分级、动态分屏视频和新增强的遮罩创建器&…

​软考-高级-信息系统项目管理师教程 第四版【第15章-项目风险管理-思维导图】​

软考-高级-信息系统项目管理师教程 第四版【第15章-项目风险管理-思维导图】 课本里章节里所有蓝色字体的思维导图