十九、类型信息(5)

news2024/11/29 1:50:31

动态代理

_代理_是基本的设计模式之一。一个对象封装真实对象,代替其提供其他或不同的操作—这些操作通常涉及到与“真实”对象的通信,因此代理通常充当中间对象。这是一个简单的示例,显示代理的结构:

interface Interface {
    void doSomething();

    void somethingElse(String arg);
}

class RealObject implements Interface {
    @Override
    public void doSomething() {
        System.out.println("doSomething");
    }

    @Override
    public void somethingElse(String arg) {
        System.out.println("somethingElse " + arg);
    }
}

class SimpleProxy implements Interface {
    private Interface proxied;

    SimpleProxy(Interface proxied) {
        this.proxied = proxied;
    }

    @Override
    public void doSomething() {
        System.out.println("SimpleProxy doSomething");
        proxied.doSomething();
    }

    @Override
    public void somethingElse(String arg) {
        System.out.println(
                "SimpleProxy somethingElse " + arg);
        proxied.somethingElse(arg);
    }
}

class SimpleProxyDemo {
    public static void consumer(Interface iface) {
        iface.doSomething();
        iface.somethingElse("bonobo");
    }

    public static void main(String[] args) {
        consumer(new RealObject());
        consumer(new SimpleProxy(new RealObject()));
    }
}

输出结果:

在这里插入图片描述

因为 consumer() 接受 Interface,所以它不知道获得的是 RealObject 还是 SimpleProxy,因为两者都实现了 Interface
但是,在客户端和 RealObject 之间插入的 SimpleProxy 执行操作,然后在 RealObject 上调用相同的方法。

当你希望将额外的操作与“真实对象”做分离时,代理可能会有所帮助,尤其是当你想要轻松地启用额外的操作时,反之亦然(设计模式就是封装变更—所以你必须改变一些东西以证明模式的合理性)。例如,如果你想跟踪对 RealObject 中方法的调用,或衡量此类调用的开销,该怎么办?你不想这部分代码耦合到你的程序中,而代理能使你可以很轻松地添加或删除它。

Java 的_动态代理_更进一步,不仅动态创建代理对象而且动态处理对代理方法的调用。在动态代理上进行的所有调用都被重定向到单个_调用处理程序_,该处理程序负责发现调用的内容并决定如何处理。这是 SimpleProxyDemo.java 使用动态代理重写的例子:

import java.lang.reflect.*;

class DynamicProxyHandler implements InvocationHandler {
    private Object proxied;

    DynamicProxyHandler(Object proxied) {
        this.proxied = proxied;
    }

    @Override
    public Object
    invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        System.out.println(
                "**** proxy: " + proxy.getClass() +
                        ", method: " + method + ", args: " + args);
        if (args != null) {
            for (Object arg : args) {
                System.out.println("  " + arg);
            }
        }
        return method.invoke(proxied, args);
    }
}

class SimpleDynamicProxy {
    public static void consumer(Interface iface) {
        iface.doSomething();
        iface.somethingElse("bonobo");
    }

    public static void main(String[] args) {
        RealObject real = new RealObject();
        consumer(real);
        // Insert a proxy and call again:
        Interface proxy = (Interface) Proxy.newProxyInstance(
                Interface.class.getClassLoader(),
                new Class[]{Interface.class},
                new DynamicProxyHandler(real));
        consumer(proxy);
    }
}

输出结果:

在这里插入图片描述

可以通过调用静态方法 Proxy.newProxyInstance() 来创建动态代理,该方法需要一个类加载器(通常可以从已加载的对象中获取),希望代理实现的接口列表(不是类或抽象类),以及接口 InvocationHandler 的一个实现。动态代理会将所有调用重定向到调用处理程序,因此通常为调用处理程序的构造函数提供对“真实”对象的引用,以便一旦执行中介任务便可以转发请求。

invoke() 方法被传递给代理对象,以防万一你必须区分请求的来源—但是在很多情况下都无需关心。但是,在 invoke() 内的代理上调用方法时要小心,因为接口的调用是通过代理重定向的。

通常执行代理操作,然后使用 Method.invoke() 将请求转发给被代理对象,并携带必要的参数。这在一开始看起来是有限制的,好像你只能执行一般的操作。但是,可以过滤某些方法调用,同时传递其他方法调用:

import java.lang.reflect.*;

class MethodSelector implements InvocationHandler {
    private Object proxied;

    MethodSelector(Object proxied) {
        this.proxied = proxied;
    }

    @Override
    public Object
    invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        if (method.getName().equals("interesting")) {
            System.out.println(
                    "Proxy detected the interesting method");
        }
        return method.invoke(proxied, args);
    }
}

interface SomeMethods {
    void boring1();

    void boring2();

    void interesting(String arg);

    void boring3();
}

class Implementation implements SomeMethods {
    @Override
    public void boring1() {
        System.out.println("boring1");
    }

    @Override
    public void boring2() {
        System.out.println("boring2");
    }

    @Override
    public void interesting(String arg) {
        System.out.println("interesting " + arg);
    }

    @Override
    public void boring3() {
        System.out.println("boring3");
    }
}

class SelectingMethods {
    public static void main(String[] args) {
        SomeMethods proxy =
                (SomeMethods) Proxy.newProxyInstance(
                        SomeMethods.class.getClassLoader(),
                        new Class[]{SomeMethods.class},
                        new MethodSelector(new Implementation()));
        proxy.boring1();
        proxy.boring2();
        proxy.interesting("bonobo");
        proxy.boring3();
    }
}

输出结果:

在这里插入图片描述

在这个示例里,我们只是在寻找方法名,但是你也可以寻找方法签名的其他方面,甚至可以搜索特定的参数值。

动态代理不是你每天都会使用的工具,但是它可以很好地解决某些类型的问题。你可以在 Erich Gamma 等人的_设计模式_中了解有关_代理_和其他设计模式的更多信息。

Optional类

如果你使用内置的 null 来表示没有对象,每次使用引用的时候就必须测试一下引用是否为 null,这显得有点枯燥,而且势必会产生相当乏味的代码。问题在于 null 没什么自己的行为,只会在你想用它执行任何操作的时候产生 NullPointExceptionjava.util.Optional(首次出现是在函数式编程这章)为 null 值提供了一个轻量级代理,Optional 对象可以防止你的代码直接抛出 NullPointException

虽然 Optional 是 Java 8 为了支持流式编程才引入的,但其实它是一个通用的工具。为了证明这点,在本节中,我们会把它用在普通的类中。因为涉及一些运行时检测,所以把这一小节放在了本章。

实际上,在所有地方都使用 Optional 是没有意义的,有时候检查一下是不是 null 也挺好的,或者有时我们可以合理地假设不会出现 null,甚至有时候检查 NullPointException 异常也是可以接受的。Optional 最有用武之地的是在那些“更接近数据”的地方,在问题空间中代表实体的对象上。举个简单的例子,很多系统中都有 Person 类型,代码中有些情况下你可能没有一个实际的 Person 对象(或者可能有,但是你还没用关于那个人的所有信息)。这时,在传统方法下,你会用到一个 null 引用,并且在使用的时候测试它是不是 null。而现在,我们可以使用 Optional

import java.util.*;

class Person {
    public final Optional<String> first;
    public final Optional<String> last;
    public final Optional<String> address;
    // etc.
    public final Boolean empty;

    Person(String first, String last, String address) {
        this.first = Optional.ofNullable(first);
        this.last = Optional.ofNullable(last);
        this.address = Optional.ofNullable(address);
        empty = !this.first.isPresent()
                && !this.last.isPresent()
                && !this.address.isPresent();
    }

    Person(String first, String last) {
        this(first, last, null);
    }

    Person(String last) {
        this(null, last, null);
    }

    Person() {
        this(null, null, null);
    }

    @Override
    public String toString() {
        if (empty) {
            return "<Empty>";
        }
        return (first.orElse("") +
                " " + last.orElse("") +
                " " + address.orElse("")).trim();
    }

    public static void main(String[] args) {
        System.out.println(new Person());
        System.out.println(new Person("Smith"));
        System.out.println(new Person("Bob", "Smith"));
        System.out.println(new Person("Bob", "Smith",
                "11 Degree Lane, Frostbite Falls, MN"));
    }
}

输出结果:

在这里插入图片描述

Person 的设计有时候又叫“数据传输对象(DTO,data-transfer object)”。注意,所有字段都是 publicfinal 的,所以没有 gettersetter 方法。也就是说,Person 是不可变的,你只能通过构造器给它赋值,之后就只能读而不能修改它的值(字符串本身就是不可变的,因此你无法修改字符串的内容,也无法给它的字段重新赋值)。如果你想修改一个 Person,你只能用一个新的 Person 对象来替换它。empty 字段在对象创建的时候被赋值,用于快速判断这个 Person 对象是不是空对象。

如果想使用 Person,就必须使用 Optional 接口才能访问它的 String 字段,这样就不会意外触发 NullPointException 了。

现在假设你已经因你惊人的理念而获得了一大笔风险投资,现在你要招兵买马了,但是在虚位以待时,你可以将 Person Optional 对象放在每个 Position 上:

import java.util.*;

class EmptyTitleException extends RuntimeException {
}

class Position {
    private String title;
    private Person person;

    Position(String jobTitle, Person employee) {
        setTitle(jobTitle);
        setPerson(employee);
    }

    Position(String jobTitle) {
        this(jobTitle, null);
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String newTitle) {
        // Throws EmptyTitleException if newTitle is null:
        title = Optional.ofNullable(newTitle)
                .orElseThrow(EmptyTitleException::new);
    }

    public Person getPerson() {
        return person;
    }

    public void setPerson(Person newPerson) {
        // Uses empty Person if newPerson is null:
        person = Optional.ofNullable(newPerson)
                .orElse(new Person());
    }

    @Override
    public String toString() {
        return "Position: " + title +
                ", Employee: " + person;
    }

    public static void main(String[] args) {
        System.out.println(new Position("CEO"));
        System.out.println(new Position("Programmer",
                new Person("Arthur", "Fonzarelli")));
        try {
            new Position(null);
        } catch (Exception e) {
            System.out.println("caught " + e);
        }
    }
}

输出结果:

在这里插入图片描述

这里使用 Optional 的方式不太一样。请注意,titleperson 都是普通字段,不受 Optional 的保护。但是,修改这些字段的唯一途径是调用 setTitle()setPerson() 方法,这两个都借助 Optional 对字段进行了严格的限制。

同时,我们想保证 title 字段永远不会变成 null 值。为此,我们可以自己在 setTitle() 方法里边检查参数 newTitle 的值。但其实还有更好的做法,函数式编程一大优势就是可以让我们重用经过验证的功能(即便是个很小的功能),以减少自己手动编写代码可能产生的一些小错误。所以在这里,我们用 ofNullable()newTitle 转换一个 Optional(如果传入的值为 nullofNullable() 返回的将是 Optional.empty())。紧接着我们调用了 orElseThrow() 方法,所以如果 newTitle 的值是 null,你将会得到一个异常。这里我们并没有把 title 保存成 Optional,但通过应用 Optional 的功能,我们仍然如愿以偿地对这个字段施加了约束。

EmptyTitleException 是一个 RuntimeException,因为它意味着程序存在错误。在这个方案里边,你仍然可能会得到一个异常。但不同的是,在错误产生的那一刻(向 setTitle()null 值时)就会抛出异常,而不是发生在其它时刻,需要你通过调试才能发现问题所在。另外,使用 EmptyTitleException 还有助于定位 BUG。

Person 字段的限制又不太一样:如果你把它的值设为 null,程序会自动把将它赋值成一个空的 Person 对象。先前我们也用过类似的方法把字段转换成 Optional,但这里我们是在返回结果的时候使用 orElse(new Person()) 插入一个空的 Person 对象替代了 null

Position 里边,我们没有创建一个表示“空”的标志位或者方法,因为 person 字段的 Person 对象为空,就表示这个 Position 是个空缺位置。之后,你可能会发现你必须添加一个显式的表示“空位”的方法,但是正如 YAGNI (You Aren’t Going to Need It,你永远不需要它)所言,在初稿时“实现尽最大可能的简单”,直到程序在某些方面要求你为其添加一些额外的特性,而不是假设这是必要的。

请注意,虽然你清楚你使用了 Optional,可以免受 NullPointerExceptions 的困扰,但是 Staff 类却对此毫不知情。

import java.util.*;

public class Staff extends ArrayList<Position> {
    public void add(String title, Person person) {
        add(new Position(title, person));
    }

    public void add(String... titles) {
        for (String title : titles) {
            add(new Position(title));
        }
    }

    public Staff(String... titles) {
        add(titles);
    }

    public Boolean positionAvailable(String title) {
        for (Position position : this) {
            if (position.getTitle().equals(title) &&
                    position.getPerson().empty) {
                return true;
            }
        }
        return false;
    }

    public void fillPosition(String title, Person hire) {
        for (Position position : this) {
            if (position.getTitle().equals(title) &&
                    position.getPerson().empty) {
                position.setPerson(hire);
                return;
            }
        }
        throw new RuntimeException(
                "Position " + title + " not available");
    }

    public static void main(String[] args) {
        Staff staff = new Staff("President", "CTO",
                "Marketing Manager", "Product Manager",
                "Project Lead", "Software Engineer",
                "Software Engineer", "Software Engineer",
                "Software Engineer", "Test Engineer",
                "Technical Writer");
        staff.fillPosition("President",
                new Person("Me", "Last", "The Top, Lonely At"));
        staff.fillPosition("Project Lead",
                new Person("Janet", "Planner", "The Burbs"));
        if (staff.positionAvailable("Software Engineer")) {
            staff.fillPosition("Software Engineer",
                    new Person(
                            "Bob", "Coder", "Bright Light City"));
        }
        System.out.println(staff);
    }
}

输出结果:

在这里插入图片描述

注意,在有些地方你可能还是要测试引用是不是 Optional,这跟检查是否为 null 没什么不同。但是在其它地方(例如本例中的 toString() 转换),你就不必执行额外的测试了,而可以直接假设所有对象都是有效的。

标记接口

有时候使用一个标记接口来表示空值会更方便。标记接口里边什么都没有,你只要把它的名字当做标签来用就可以。

// onjava/Null.java
package onjava;
public interface Null {}

如果你用接口取代具体类,那么就可以使用 DynamicProxy 来自动地创建 Null 对象。假设我们有一个 Robot 接口,它定义了一个名字、一个模型和一个描述 Robot 行为能力的 List<Operation>

import java.util.*;

public interface Robot {
    String name();

    String model();

    List<Operation> operations();

    static void test(Robot r) {
        if (r instanceof Null) {
            System.out.println("[Null Robot]");
        }
        System.out.println("Robot name: " + r.name());
        System.out.println("Robot model: " + r.model());
        for (Operation operation : r.operations()) {
            System.out.println(operation.description.get());
            operation.command.run();
        }
    }
}

你可以通过调用 operations() 来访问 Robot 的服务。Robot 里边还有一个 static 方法来执行测试。

Operation 包含一个描述和一个命令(这用到了命令模式)。它们被定义成函数式接口的引用,所以可以把 lambda 表达式或者方法的引用传给 Operation 的构造器:

import java.util.function.*;

public class Operation {
    public final Supplier<String> description;
    public final Runnable command;

    public Operation(Supplier<String> descr, Runnable cmd) {
        description = descr;
        command = cmd;
    }
}

现在我们可以创建一个扫雪 Robot

import java.util.*;

public class SnowRemovalRobot implements Robot {
    private String name;

    public SnowRemovalRobot(String name) {
        this.name = name;
    }

    @Override
    public String name() {
        return name;
    }

    @Override
    public String model() {
        return "SnowBot Series 11";
    }

    private List<Operation> ops = Arrays.asList(
            new Operation(
                    () -> name + " can shovel snow",
                    () -> System.out.println(
                            name + " shoveling snow")),
            new Operation(
                    () -> name + " can chip ice",
                    () -> System.out.println(name + " chipping ice")),
            new Operation(
                    () -> name + " can clear the roof",
                    () -> System.out.println(
                            name + " clearing roof")));

    @Override
    public List<Operation> operations() {
        return ops;
    }

    public static void main(String[] args) {
        Robot.test(new SnowRemovalRobot("Slusher"));
    }
}

输出结果:

在这里插入图片描述

假设存在许多不同类型的 Robot,我们想让每种 Robot 都创建一个 Null 对象来执行一些特殊的操作——在本例中,即提供 Null 对象所代表 Robot 的确切类型信息。这些信息是通过动态代理捕获的:

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

class NullRobotProxyHandler implements InvocationHandler {
    private String nullName;
    private Robot proxied = new NRobot();

    NullRobotProxyHandler(Class<? extends Robot> type) {
        nullName = type.getSimpleName() + " NullRobot";
    }

    private class NRobot implements Null, Robot {
        @Override
        public String name() {
            return nullName;
        }

        @Override
        public String model() {
            return nullName;
        }

        @Override
        public List<Operation> operations() {
            return Collections.emptyList();
        }
    }

    @Override
    public Object
    invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        return method.invoke(proxied, args);
    }
}

public class NullRobot {
    public static Robot
    newNullRobot(Class<? extends Robot> type) {
        return (Robot) Proxy.newProxyInstance(
                NullRobot.class.getClassLoader(),
                new Class[]{Null.class, Robot.class},
                new NullRobotProxyHandler(type));
    }

    public static void main(String[] args) {
        Stream.of(
                new SnowRemovalRobot("SnowBee"),
                newNullRobot(SnowRemovalRobot.class)
        ).forEach(Robot::test);
    }
}

输出结果:

在这里插入图片描述

无论何时,如果你需要一个空 Robot 对象,只需要调用 newNullRobot(),并传递需要代理的 Robot 的类型。这个代理满足了 RobotNull 接口的需要,并提供了它所代理的类型的确切名字。

Mock 对象和桩

Mock 对象和 **桩(Stub)**在逻辑上都是 Optional 的变体。他们都是最终程序中所使用的“实际”对象的代理。不过,Mock 对象和桩都是假扮成那些可以传递实际信息的实际对象,而不是像 Optional 那样把包含潜在 null 值的对象隐藏。

Mock 对象和桩之间的的差别在于程度不同。Mock 对象往往是轻量级的,且用于自测试。通常,为了处理各种不同的测试场景,我们会创建出很多 Mock 对象。而桩只是返回桩数据,它通常是重量级的,并且经常在多个测试中被复用。桩可以根据它们被调用的方式,通过配置进行修改。因此,桩是一种复杂对象,它可以做很多事情。至于 Mock 对象,如果你要做很多事,通常会创建大量又小又简单的 Mock 对象。

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

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

相关文章

【Java 进阶篇】Java Web开发:实现验证码功能

在Web应用程序中&#xff0c;验证码&#xff08;CAPTCHA&#xff09;是一种常见的安全工具&#xff0c;用于验证用户是否为人类而不是机器。验证码通常以图像形式呈现&#xff0c;要求用户在登录或注册时输入正确的字符。在这篇文章中&#xff0c;我们将详细介绍如何在Java Web…

NEFU离散数学实验特别篇1-树和图

树相关概念 离散数学中&#xff0c;树是一种重要的数据结构&#xff0c;它是一种无向连通图&#xff0c;并且不存在环。下面是树的相关概念和公式&#xff1a; 1. 顶点数为n的树&#xff0c;边数为n-1。 2. 度数为k的树中有k个分支。 3. 一棵树中最多只有两个度数大于1的顶点&a…

京东API获得JD商品详情 item_get-获得JD商品详情

item_get-获得JD商品详情 公共参数 名称类型必须描述keyString是调用key&#xff08;必须以GET方式拼接在URL中&#xff09;获取key和secret接入secretString是调用密钥api_nameString是API接口名称&#xff08;包括在请求地址中&#xff09;[item_search,item_get,item_sear…

面经(面试经验)第一步,从自我介绍开始说起

看到一位同学讲自己的面试步骤和过程&#xff0c;我心有所感&#xff0c;故此想整理下面试的准备工作。以便大家能顺利应对面试&#xff0c;通过面试... 求职应聘找工作&#xff0c;面试是必然的关卡&#xff0c;如今竞争激烈呀&#xff0c;想要得到自己喜欢的工作&#xff0c…

U盘装系统Win10步骤和详细教程

如果我们遇到系统问题&#xff0c;导致无法正常使用系统了&#xff0c;这时候就可以考虑给电脑重新安装系统。有用户想用U盘来进行系统的重装&#xff0c;但不清楚具体重装步骤&#xff0c;下面小编给大家详细介绍关于U盘重装系统Win10的详细步骤方法&#xff0c;帮助大家简单轻…

请求转发和重定向的区别

目录 一、请求转发 二、重定向 一、请求转发 request.getRequestDispatcher(页面).forwad(request,response) 请求转发&#xff1a; 客户端&#xff08;浏览器&#xff09;向服务器的资源 A 发起一次请求 ①。服务器的资源 A 接收到该请求后&#xff0c;将该请求转发到内部的…

FedAT:异步更新联邦学习方法

文章链接&#xff1a;FedAT: A Communication-Efficient Federated Learning Method with Asynchronous Tiers under Non-IID Data 发表会议: SC’21 (International Conference for High Performance Computing, Networking, Storage, and Analysis) 高性能计算&#xff0c;体…

高能有料 | 第二届OpenHarmony技术大会议程速递

第二届开放原子开源基金会OpenHarmony技术大会如约而至 让我们一起 开封无限惊喜的技术成果 开放无限前沿的议题干货 开启无限可能的未来之门 点击此处报名参会&#xff01;

接口自动化测试设计!

一、接口测试基础 1.什么是接口测试&#xff1f; 接口测试是测试系统组件间接口的一种测试。接口测试主要用于检测外部系统与系统之间以及内部各个子系统之间的交互点。接口测试的重点是要检查数据的交换&#xff0c;传递和控制管理过程&#xff0c;以及系统间的相互逻辑依赖…

01【保姆级】-GO语言特点 下载安装 hello

01-GO语言基本概念和安装使用 一、概念1.1 Go语言的诞生1.2 GO语言的特点&#xff1a; 二、安装go2.1 安装2.2 安装环境变量 三、下载&安装goland3.1 官网下载3.2 下载后&#xff0c;进行安装&#xff1a; 四、Go语言的特性&#xff08;重点&#xff09;4.1 第一个hello wo…

Spire.doc读取模板文档,并在书签处插入内容

在书签位置插入文字 //加载模板文档 Document document new Document(Server.MapPath("~/File/评价结果.doc")); //创建书签导航器 BookmarksNavigator bn new BookmarksNavigator(document); //添加一个section到文档 Section newSec document.AddSection(); …

Redis Sentinel 哨兵模式

Sentinel 哨兵模式 Redis Sentinel 官网 Redis 的 Sentinel 文档 -- Redis中国用户组&#xff08;CRUG&#xff09; Sentinel Redis 命令参考&#xff08;红色&#xff09; Sentinel 通过监控的方式获取主机的工作状态是否正常&#xff0c;当主机发生故障时&#xff0c; Senti…

生产制造业如何进行进销存管理?建议收藏!

进销存是什么&#xff1f;生产制造业如何进行进销存管理&#xff1f;制造进销存都能为企业提供什么&#xff1f;进销存管理系统的优势&#xff1f;本文将带大家深入浅出的聊聊制造进销存&#xff0c;全面剖析制造进销存的前世今生&#xff0c;并且为大家提供2023年十大制造进销…

「linux基础」上传代码到github/gitee

一、在gitee创建一个仓库 1.创建仓库 2.获取仓库地址 二、克隆仓库文件到linux中 1.查看Linux中是否安装git&#xff1a;git --version 如果没有&#xff0c;在root下使用指令 yum install -y git 安装。 2.使用 git clone 仓库地址&#xff0c;克隆仓库文件到linux中 三、第…

第一章 02Java入门-常环境变量的意义

前言 上次我们学习了常见的CMD命令,这次我们做一个用它做一个练习打开QQ(CMD方式打开),最后引出环境变量的意义。 一、CMD打开qq 可以看到,如果直接在CMD里面打开QQ,是不可以的,因为QQ的路径不在默认路径C盘,而在D盘下面的develop文件夹下面的qq下面的qq.exe下(自己…

软考高级之系统架构师之计算专题

操作系统 需要一定操作系统基础知识才能计算的题目。 吞吐率 某指令流水线由5段组成&#xff0c;各段所需要的时间如下图所示。连续输入100条指令时的吞吐率为&#xff08;&#xff09;。 解析&#xff1a; 指令流水线的吞吐率定义为&#xff1a; 吞吐率 T P 指令数 执行时…

Gcov 查看代码覆盖率

GCOV 工具简介 gcov是一个测试代码覆盖率的工具。 它是 gcc 自带的查看代码覆盖率的工具&#xff0c;无需额外安装&#xff0c;在嵌入式的 arm-eabi-none-gcc 中同样可以使用&#xff08;需要重写部分系统函数&#xff09;。 使用效果如下图所示&#xff1a; 程序运行完成后…

云尘-Potato(Suncsr)1

vulhub的题目 继续渗透吧 我们依旧打开nmap 开扫 首先扫描目标网段nmap -sP 127.25.0.1/24 nmap -sS -sV -p- -v 172.25.0.13然后扫描端口 获取到两个 80和 7120 80机会很多 22就一次 爆破弱口令 报完没有就去80测试 hydra -l potato -P 字典 -vV ssh://172.25.0.13:7120 …

MySQL数据库入门到精通——运维篇(1)

MySQL数据库入门到精通——运维篇&#xff08;1&#xff09; 1. 日志1.1 错误日志1.2 二进制日志1.3 查询日志1.4 慢查询日志 2. 主从复制2.1 主从复制的概述2.2 主从复制的原理2.3 主从复制的搭建2.3.1 服务器准备2.3.2 主库配置2.3.3 从库配置2.3.4 测试 1. 日志 在任何一种…

轻量封装WebGPU渲染系统示例<11>- WebGP实现的简单PBR效果(源码)

当前示例源码github地址: https://github.com/vilyLei/voxwebgpu/blob/main/src/voxgpu/sample/SimplePBRTest.ts 此示例渲染系统实现的特性: 1. 用户态与系统态隔离。 2. 高频调用与低频调用隔离。 3. 面向用户的易用性封装。 4. 渲染数据和渲染机制分离。 5. 用户操作…