初识Java 17-2 反射

news2024/11/15 10:36:50

目录

转型前检查

构建例子:生成层次结构

优化Creator:使用类字面量

优化PetCounter:动态验证类型

更通用的递归计数

注册工厂


本笔记参考自: 《On Java 中文版》


转型前检查

        当我们使用传统的类型转换,例如:

Object x = new Shape();
Shape s = (Shape)x; // 传统的类型转换

此时Java就会执行反射,这种转型通常被称为“类型安全向下转型”。不同于向上转型,编译器不会知道这个Object实际的类型是什么。因此在向下转型时,我们需要声明对象的确切类型

    C++在这种强制类型转换时不会进行反射,而是进行“运行时类型识别”。告诉编译器,这是一个新的类型。

        而除了这种类型转换和Class对象外,Java还提供了其他反射的方式:instanceof关键字。这一关键字可以用于判断对象是否是特定类型的实例,例如:

【例子:instanceof关键字的使用】

public class ClassInstanceof {
    public static void main(String[] args) {
        Object obj = new Circle();
        if (obj instanceof Shape)
            System.out.println("这个实例是一个Shape");
        if (obj instanceof Circle)
            System.out.println("这个实例是一个Circle");

        if (obj instanceof Square)
            System.out.println("这个实例是一个Square");
        else
            System.out.println("这个实例的类型不是Square");
    }
}

        程序执行的结果是:

        instanceof的存在使得程序员可以使用Java轻松识别出对象的正确类型。

---

构建例子:生成层次结构

        为了方便后续的示例,这里先创建一个比较复杂的层次结构(人和宠物):

        在这一章的笔记中,只会使用到Pet类及其子类。但为了结构的完整性,这里展示的是整个继承结构。Individual作为所有类的基类,会被用到的部分只有其重写的toString()

@Override
public String toString() {
    return getClass().getSimpleName() +
            (name == null ? "" : " " + name);
}

        Pet及其子类的实现都较为简单,这里只选取其中一条分支进行展示(其他均与该分支相同):

        这些类都有一个无参构造器,这样我们就可以调用newInstacnce()来生成实例。下面的例子是一个用于随机生成Pet对象的工具类:

【例子:随机生成Pet对象】

package reflection.pets;

import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public abstract class Creator
        implements Supplier<Pet> {
    private Random random = new Random(47);

    // types()方法用于创建不同的Pet对象,
    // 我们要求这个方法必须在子类中进行实现
    // (一般,这个方法只需要返回一个静态的List引用即可)
    public abstract List<Class<? extends Pet>> types();

    // 创建一个随机的Pet对象:
    @Override
    public Pet get() {
        int n = random.nextInt(types().size());
        try {
            return types().get(n)
                    .getConstructor().newInstance();
            // 调用newInstance(),会得到四个异常:
        } catch (InstantiationException |
                 NoSuchMethodException |
                 InvocationTargetException |
                 IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }

    public Stream<Pet> stream() {
        return Stream.generate(this);
    }

    public Pet[] array(int size) { // 调用上面的stream()方法
        return stream().limit(size).toArray(Pet[]::new);
    }

    public List<Pet> list(int size) {
        return stream().limit(size) // 将元素收集到一个ArrayList中
                .collect(Collectors.toCollection(ArrayList::new));
    }
}

        Creator是一个生成器的基类,它的types()是一个抽象方法,这样我们就可以要求Creator的基类去实现这个方法。这是一个模板方法模式的例子。值得一提的是,types()的返回类型被规定成一个包含Class对象的List,这个List可以接受Pet及其的任意子类

        接下来尝试为Creator实现一个子类:

【例子:使用forName()的生成器】

package reflection.pets;

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

public class ForNamePetCreator extends Creator {
    // 应该确保:在类被初始化时,就向manyTypes列表中加载数据
    // (也就是说,在类被加载时就应该调用loader()方法)
    private static List<Class<? extends Pet>> manyTypes =
            new ArrayList<>();

    // 设置我们需要生成的类型:
    private static String[] typeNames = {
            // Dog的子类:
            "reflection.pets.Mutt",
            "reflection.pets.Pug",
            // Cat的子类:
            "reflection.pets.Chero",
            "reflection.pets.Manx",
            "reflection.pets.Cymric",
            // Rodent的子类:
            "reflection.pets.Rat",
            "reflection.pets.Mouse",
            "reflection.pets.Hamster"
    };

    @SuppressWarnings("unchecked")
    private static void loader() {
        try {
            for (String name : typeNames)
                manyTypes.add(
                        (Class<? extends Pet>) Class.forName(name));
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
    }

    static {
        loader();
    }

    // types()方法只需要用于返回静态的List引用即可
    @Override
    public List<Class<? extends Pet>> types() {
        return manyTypes;
    }
}

        通过Class.forName(),我们就设置好的类的完全限定名全部录入到manyTypes列表中。因为我们放入forName()中的是在编译时无法被验证的字符串,因此它可以会报错。

        需要提一下这里的静态初始化块:

static {
    loader();
}

由于@SuppressWarnings()不允许被直接用于静态初始化块,因此我们需要将loader()放入一个静态初始化块中。这样当类第一次被使用时,loader()就会被唯一一次调用,保证manyTypes()被正确初始化。

        instanceof关键字可用于检测对象的类型,下面的例子会通过这一关键字完成对各种Pet类型的数量监控。因为需要反映各个类型与其数量之间的对应关系,所以我们可以使用Map

【例子:监控各个Pet类型的数量】

package reflection;

import java.util.HashMap;

import reflection.pets.*;

// 用于跟踪各种类的Pet数量
public class PetCounter {
    static class Counter extends HashMap<String, Integer> {
        public void count(String type) {
            // get()返回type映射的值,若type不在Map中,返回null
            Integer quantity = get(type);
            if (quantity == null) {
                // 将键和值进行绑定
                put(type, 1);
            } else
                put(type, quantity + 1);
        }
    }

    private Counter counter = new Counter();

    private void countPet(Pet pet) {
        System.out.print(
                pet.getClass().getSimpleName() + " ");
        // 注意:instanceof只能与命名类型比较
        if (pet instanceof Pet)
            counter.count("Pet");

        // 属于Dog的:
        if (pet instanceof Dog)
            counter.count("Dog");
        if (pet instanceof Mutt)
            counter.count("Mutt");
        if (pet instanceof Pug)
            counter.count("Pug");

        // 属于Cat的:
        if (pet instanceof Cat)
            counter.count("Cat");
        if (pet instanceof Chero)
            counter.count("Chero");
        if (pet instanceof Manx)
            counter.count("Manx");
        if (pet instanceof Cymric)
            counter.count("Cymric");

        // 属于Rodent的:
        if (pet instanceof Rodent)
            counter.count("Rodent");
        if (pet instanceof Rat)
            counter.count("Rat");
        if (pet instanceof Mouse)
            counter.count("Mouse");
        if (pet instanceof Hamster)
            counter.count("Hamster");
    }

    public void count(Creator creator) {
        creator.stream().limit(20)
                .forEach(pet -> countPet(pet));
        System.out.println();
        System.out.println(counter);
    }

    public static void main(String[] args) {
        new PetCounter().count(new ForNamePetCreator());
    }
}

        程序执行的结果是:

        这个例子体现了instanceof的局限性:通过这一关键字,一个对象只能与命名类型进行比较,而不能与一个Class对象进行比较。这意味着,我们不能将我们需要比对的类型放入一个Class数组中,再通过循环等方式实现instanceof的自动化。

    不过,若代码中存在很多instanceof,这个代码可能也是存在问题的。


优化Creator:使用类字面量

        可以使用类字面量的方式优化Creator,通过这种方式实现的Creator会更加清晰:

【例子:重新实现Creator

package reflection.pets;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;

// 使用字类面量重新实现Creator
public class PetCreator extends Creator {
    // 因为使用的是字面量,所以不需要生成实例
    public static final
    List<Class<? extends Pet>> ALL_TYPES =
            Collections.unmodifiableList(Arrays.asList(
                    Pet.class, Dog.class, Cat.class, Rodent.class,
                    Mutt.class, Pug.class,
                    Chero.class, Manx.class, Cymric.class,
                    Rat.class, Mouse.class, Hamster.class
            ));

    // 进行类型的随机生成(限定:只生成子类)
    private static final
    List<Class<? extends Pet>> TYPES =
            ALL_TYPES.subList(
                    ALL_TYPES.indexOf(Mutt.class),
                    ALL_TYPES.size());

    @Override
    public List<Class<? extends Pet>> types() {
        return TYPES;
    }

    public static void main(String[] args) {
        System.out.println(TYPES);
        List<Pet> pets = new PetCreator().list(7);
        System.out.println(pets);
    }
}

        程序执行的结果是:

        因为使用的是字面量的缘故,不会用到newInstance()进行实例生成。也因此省去了处理异常的功夫。

        ALL_TYPES包含了所有的Pet类型,方便任何继承了这个类的子类进行使用。而types()返回的TYPES则包含了所有确切的宠物类型,因此可用于生成随机的Pet

    Collections.unmodifiableList()会根据传入的原始列表返回一个只读视图,其空间开辟在堆上。Java 9还加入了一个List.of()方法,这个方式生成的视图存在于内存中,因此速度会更快一点。

        因为之前的PetCounter.count()接受的是一个Creator参数,因此我们也可以使用它来测试这个新的PetCreator

【例子:测试PetCreator

package reflection;

import reflection.pets.PetCreator;

public class PetCounter2 {
    public static void main(String[] args) {
        new PetCounter().count(new PetCreator());
    }
}

        程序执行的结果是:


优化PetCounter:动态验证类型

        很显然,PetCounter中的instanceof过于冗长,而Class.isInstance()提供了一种更好的方式动态验证对象的类型,可以用它替代PetCounter中的instanceof

【例子:使用isInstance()

package reflection;

import onjava.Pair;
import reflection.pets.Pet;
import reflection.pets.PetCreator;

import java.util.HashMap;
import java.util.stream.Collectors;

public class PetCounter3 {
    static class Counter
            extends HashMap<Class<? extends Pet>, Integer> {
        Counter() {
            super(PetCreator.ALL_TYPES.stream()
                    .map(type -> Pair.make(type, 0)) // 将所有的Pet类型的计数初始化为0
                    .collect(
                            Collectors.toMap(Pair::key, Pair::value)));
        }

        public void count(Pet pet) {
            // 使用Cass.isInstance()消除instanceof
            entrySet().stream() // entrySet():返回由包含在这个Map视图中的元素决定的Set视图
                    .filter(pair -> pair.getKey().isInstance(pet))
                    .forEach(pair ->
                            put(pair.getKey(), pair.getValue() + 1));
        }

        @Override
        public String toString() {
            String result = entrySet().stream()
                    .map(pair -> String.format("%s=%s",
                            pair.getKey().getSimpleName(),
                            pair.getValue()))
                    .collect(Collectors.joining(", "));
            return "{" + result + "}";
        }
    }

    public static void main(String[] args) {
        Counter petCount = new Counter();
        new PetCreator().stream()
                .limit(20)
                .peek(petCount::count) // 对每个流元素进行计数
                .forEach(p -> System.out.print(
                        p.getClass().getSimpleName() + " "));
        System.out.println("\n" + petCount);
    }
}

        程序执行的结果是:

        isInstance()的使用使得我们不再需要使用instanceof

        为了能够为所有的Pet类型计数,就需要在创建类的时候预加载所有的Pet类。因此内部类Counter就需要在其创建的时候将所有类型进行加载,这就用到了之前设置的ALL_TYPES。也因此,只需要修改ALL_TYPES就可以增删类型。


更通用的递归计数

        上一个例子需要在使用前加载所有的Pet类型,但我们也可以使用isAssignableFrom()方法来替代这一预加载过程,在方法执行时对每一个将要计数的对象进行类型判断。这样,我们就可以获得一个更加通用的计数方法:

【例子:对某一类型的实例进行计数】

package onjava;

import java.util.HashMap;
import java.util.stream.Collectors;

public class TypeCounter
        extends HashMap<Class<?>, Integer> {
    private Class<?> baseType;

    public TypeCounter(Class<?> baseType) {
        this.baseType = baseType;
    }

    public void count(Object obj) {
        Class<?> type = obj.getClass();

        if (!baseType.isAssignableFrom(type))
            throw new RuntimeException(
                    obj + "是一个错误的类型:" + type +
                            ",应该传入" + baseType + "或其的子类");
        countClass(type);
    }

    // 先对基类进行计数。若存在父类,则递归调用自身,对父类进行计数。
    private void countClass(Class<?> type) {
        Integer quantity = get(type);
        put(type, quantity == null ? 1 : quantity + 1);
        Class<?> superClass = type.getSuperclass();
        if (superClass != null &&
                baseType.isAssignableFrom(superClass)) {
            countClass(superClass);
        }
    }

    @Override
    public String toString() {
        String result = entrySet().stream()
                .map(pair -> String.format("%s=%s",
                        pair.getKey().getSimpleName(),
                        pair.getValue()))
                .collect(Collectors.joining(", "));
        return "{" + result + "}";
    }
}

        isAssignableFrom()方法用于验证传递的对象是否存在于要求的层次结构中。

        在上述的类中,countClass()方法就是用于递归计数的,它在完成子类的计数后会进行判断,若存在父类,则对父类进行计数。

        现在,可以使用上面的类了:

【例子:使用TypeCounter

package reflection;

import onjava.TypeCounter;
import reflection.pets.Pet;
import reflection.pets.PetCreator;

public class PetCounter4 {
    public static void main(String[] args) {
        TypeCounter counter = new TypeCounter(Pet.class);
        new PetCreator().stream()
                .limit(20)
                .peek(counter::count)
                .forEach(pet -> System.out.print(
                        pet.getClass().getSimpleName() + " "));
        System.out.println("\n" + counter);
    }
}

        程序执行的结果如下:

注册工厂

        在上述例子实现Creator的时候,还存在一个问题:

    若需要为Pet添加一个新的子类,就需要在Creator的实现PetCreator等)中添加对应的类型。若我们需要较为频繁地添加子类,这就会给我们造成一定的麻烦。

        对于这种情况,一般有两种做法:

  1. 手动创建列表,并将这个列表放在较为核心的代码部分,例如基类之中。
  2. 使用工厂方法设计模式推迟对象的创建,将创建交付给类自己去完成。工厂方法可以被多态地调用,来创建恰当类型的对象。

        java.util.function.SupplierT get()方法就是一个工厂方法的原型。这一方法可以通过协变返回类型(基类方法返回值的子类型)完成对象的返回。

        下面的示例包含一个工厂对象(Supplier<Part>)的静态List

【例子:使用工厂方法进行注册】

package reflection;

import java.util.Arrays;
import java.util.List;
import java.util.Random;
import java.util.function.Supplier;
import java.util.stream.Stream;

class Part implements Supplier<Part> {
    @Override
    public String toString() {
        return getClass().getSimpleName();
    }

    static List<Supplier<? extends Part>> prototypes =
            Arrays.asList( // 将类型的工厂类添加到列表中,完成“注册”
                    new FuelFilter(),
                    new AirFilter(),
                    new CabinAirFilter(),
                    new OilFilter(),
                    new FanBelt(),
                    new PowerSteeringBelt(),
                    new GeneratorBelt());

    private static Random rand = new Random(47);

    @Override
    public Part get() {
        int n = rand.nextInt(prototypes.size());
        // 第一个get(n)调用自Arrays
        // 第二个get()才是Supplier的
        return prototypes.get(n).get();
    }
}

// 分类器,不存在实现
class Filter extends Part {
}

class FuelFilter extends Filter {
    @Override
    public FuelFilter get() {
        return new FuelFilter();
    }
}

class AirFilter extends Filter {
    @Override
    public AirFilter get() {
        return new AirFilter();
    }
}

class CabinAirFilter extends Filter {
    @Override
    public CabinAirFilter get() {
        return new CabinAirFilter();
    }
}

class OilFilter extends Filter {
    @Override
    public OilFilter get() {
        return new OilFilter();
    }
}

// 分类器,不存在实现
class Belt extends Part {
}

class FanBelt extends Belt {
    @Override
    public FanBelt get() {
        return new FanBelt();
    }
}

class GeneratorBelt extends Belt {
    @Override
    public GeneratorBelt get() {
        return new GeneratorBelt();
    }
}

class PowerSteeringBelt extends Belt {
    @Override
    public PowerSteeringBelt get() {
        return new PowerSteeringBelt();
    }
}

public class RegisteredFactories {
    public static void main(String[] args) {
        Stream.generate(new Part())
                .limit(10)
                .forEach(System.out::println);
    }
}

        程序执行的结果是:

        先观察列表prototypes

这些子类原本都应该由get()方法进行生成,它们的工厂类都被添加到了列表中,通过这种方式,我们完成了“注册”。这些工厂是对象本身的实例,在实际操作中,它们会作为生成其他对象的模板进行使用。

    这些对象就好比一个一个的工厂,它们会分别生产不同的“零件”。通过调用这些类的方法,我们就可以进行“零件”的生产。

        上述的FilterBelt用于对不同的“零件”进行分类,因此它们不需要具体的实现。

        这种做法的好处可以在main()方法中看见:由于Part类的get()方法可以生成各种各样的零件,我们仅需创建Part对象就可以控制各个“零件”的生产。

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

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

相关文章

实战!工作中常用的设计模式

文章目录 前言一、策略模式1.1、 业务场景1.2 、策略模式定义1.3、 策略模式使用1.3.1、一个接口&#xff0c;两个方法1.3.2、不同策略的差异化实现1.3.3、使用策略模式 二、责任链模式2.1、业务场景2.2、责任链模式定义2.3、责任链模式使用2.3.1、一个接口或者抽象类2.3.2、每…

11.7加减计数器,可置位~,数字钟分秒,串转并,串累加转并,24位串并128,流水乘法器,一些乘法器

信号发生器 方波&#xff0c;就是一段时间内都输出相同的信号 锯齿波就是递增 三角波就是先增后减 加减计数器 当mode为1则加&#xff0c;Mode为0则减&#xff1b;只要为0就输出zero 这样会出问题&#xff0c;因为要求是十进制&#xff0c;但是这里并没有考虑到9之后怎么办&a…

openvino学习(一)ubuntu20.04安装openvino2022

安装openvino2022要求 操作系统 Ubuntu 18.04 长期支持 (LTS)&#xff0c;64 位 Ubuntu 20.04 长期支持 (LTS)&#xff0c;64 位 软件 CMake 3.13 或更高版本&#xff0c;64 位 GCC 7.5.0&#xff08;适用于 Ubuntu 18.04&#xff09;或 GCC 9.3.0&#xff08;适用于 Ubunt…

工具介绍——第三方软件远程连接(工具:Rustdesk)

文章目录 前言一、使用工具二、开始演示1、拿下目标主机权限后上传文件2、运行目标主机上的rustdesk-1.1.9.exe文件3、目标主机上whoami查看现在的用户4、查找目标主机上连接的文件&#xff0c;并添加连接密码5、目标主机重启rustdesk的应用程序6、本地连接主机 前言 这里主要…

“第六十三天”

这两天怎么做的这么别扭&#xff0c;为什么我的vs 的strlen函数包括终止字符了&#xff1b; 哦哦&#xff0c;明白了&#xff0c;fgets函数读取在未达到指定字长&#xff0c;或者遇见空白符之前&#xff0c;会读取前面的所有字符&#xff0c;所以会读取换行符&#xff0c;而get…

康耐视深度学习ViDi-View菜单介绍

Accept View承认当前图片标注的有效性 Clear Marking 清除当前图片的标注特征 Clear Marking & Labels清除当前图片的标注特征和标签 Process处理当前图片 Edit ROI编辑检测的区域 Edit Regions编辑(标注)特征区域 Edit Mask 编辑遮挡(屏蔽)区域 Apply Mask To Tool将遮挡(…

杨辉三角(Java实现)

public class Demo09 {public static void main(String[] args) {//第一位和最后一位都为1//除了每行的第一列之外,其余的数为a[i][j]aa[i-1][j]a[i-1][j-1];int[][] arrays new int[10][10];for(int i 0;i<10;i){if(i0){ //第一行直接赋值为0arrays[i][0]1;continue;//重…

深入理解强化学习——多臂赌博机:基于置信度上界的动作选择

分类目录&#xff1a;《深入理解强化学习》总目录 因为对动作—价值的估计总会存在不确定性&#xff0c;所以试探是必须的。贪心动作虽然在当前时刻看起来最好&#xff0c;但实际上其他一些动作可能从长远看更好。 ϵ − \epsilon- ϵ−贪心算法会尝试选择非贪心的动作&#xf…

ZipInputStream解压报错java.lang.IllegalArgumentException: MALFORMED

背景 使用jdk自带的zip工具ZipInputStream&#xff0c;去读取使用winrar在中文windows制作出来的zip文件报错。 Exception in thread "main" java.lang.IllegalArgumentException: MALFORMEDat java.util.zip.ZipCoder.toString(ZipCoder.java:58)at java.util.zip…

Qwt QwtWheel绘制滚动轮

1.简介 QwtWheel 是一个用于实现滚动轮控件的类库。它基于 Qt 框架&#xff0c;并提供了一些方便的功能来处理滚动轮的事件和绘图。 QwtWheel 类继承自 QWidget类&#xff0c;用于定义滚动轮控件的通用行为。QwtWheel 添加了特定于滚动轮的功能。 QwtWheel 可以用于创建具有滚…

Spring Cloud之多级缓存

目录 传统缓存 多级缓存 JVM进程缓存 Caffeine 缓存驱逐策略 实现进程缓存 常用Lua语法 数据类型 变量声明 循环使用 定义函数 条件控制 安装OpenResty 实现Nginx业务逻辑编写 请求参数解析 实现lua访问tomcat JSON的序列化和反序列化 Tomcat的集群负载均衡 …

云表平台突破传统,企业级低代码让软件开发速度提升

随着数字化进程的加速推进&#xff0c;软件开发效率和成本的要求也在日益提高。在这个背景下&#xff0c;低代码技术的出现为企业软件开发提供了新的解决方案。低代码开发平台以其简单易用、高效灵活的特点&#xff0c;已经成为各行各业企业进行应用开发的首选工具。 企业中低代…

生成无损压缩png和有损压缩png的做法

作者:朱金灿 来源:clever101的专栏 为什么大多数人学不会人工智能编程?>>> png是一种常用的图像格式。png一般为无损压缩,但是可以是有损压缩的。 下图都是100x100的png图像,一个是无损压缩,一个是有损压缩。 看着效果基本一样,但是它们的大小相差很大,无损…

个体诊所管理系统电子处方软件,个体诊所人员服务软件,佳易王电子处方开单系统

个体诊所管理系统电子处方软件&#xff0c;个体诊所人员服务软件&#xff0c;佳易王电子处方开单系统 软件功能&#xff1a; 1、常用配方模板&#xff1a;可以自由添加配方分类&#xff0c;预先设置药品配方。 2、正常开药&#xff1a;可以灵活选择药品&#xff0c;用法用量&…

Qt 4.8.6 的下载与安装

Qt 4.8.6 的下载与安装 Qt 4.8.6 的下载与安装下载并解压 MinGW 4.8.2Qt4.8.6 库的安装Qt Creator 3.3.0 的安装配置 Qt Creator测试 官方博客&#xff1a;https://www.yafeilinux.com/ Qt开源社区&#xff1a;https://www.qter.org/ Qt 4.8.6 的下载与安装 学习《Qt Creato…

链表题(1)

链表题 今天给大家带来道链表题的练习 链表的中间节点 先给大家奉上链接&#xff1a; https://leetcode.cn/problems/middle-of-the-linked-list/description/ 题目描述; 给你单链表的头结点 head &#xff0c;请你找出并返回链表的中间结点。 如果有两个中间结点&#xff0…

day4作业

设计一个Per类&#xff0c;类中包含私有成员:姓名、年龄、指针成员身高、体重&#xff0c;再设计一个Stu类&#xff0c;类中包含私有成员:成绩、Per类对象p1&#xff0c;设计这两个类的构造函数、析构函数和拷贝构造函数、拷贝赋值函数。 #include <iostream>using name…

一句话说明:企业架构框架鼻祖Zachman

问&#xff1a;禁止废话&#xff0c;一句话表达&#xff0c;Zachman是什么&#xff1f;包含哪些内容&#xff1f; 韩老师正经回答&#xff1a;Zachman是企业架构框架鼻祖&#xff0c;包含6行6列的矩阵式架构内容。6列是5W1H&#xff08;What、How、Where、Who、When、Why&…

技术分享 | app自动化测试(Android)--App 控件定位

客户端的页面通过 XML 来实现 UI 的布局&#xff0c;页面的 UI 布局作为一个树形结构&#xff0c;而树叶被定义为节点。这里的节点也就对应了要定位的元素&#xff0c;节点的上级节点&#xff0c;定义了元素的布局结构。在 XML 布局中可以使用 XPath 进行节点的定位。 App的布…

物联网AI MicroPython学习之语法 uhashlib哈希算法

学物联网&#xff0c;来万物简单IoT物联网&#xff01;&#xff01; uhashlib 介绍 实现二进制数据散列算法&#xff0c;支持sha256&#xff0c;sha1&#xff0c;MD5。 接口介绍 sha256 - 创建一个SHA256哈希对象 参数原型&#xff1a;hash_obj uhashlib.sha256([bytes]) …