十九、类型信息(2)

本章概要

  • Class 对象
    • 类字面常量
    • 泛化的 Class 引用
    • cast() 方法

Class 对象

要理解 RTTI 在 Java 中的工作原理,首先必须知道类型信息在运行时是如何表示的。这项工作是由称为 **Class**对象 的特殊对象完成的,它包含了与类有关的信息。实际上,Class 对象就是用来创建该类所有"常规"对象的。Java 使用 Class 对象来实现 RTTI,即便是类型转换这样的操作都是用 Class 对象实现的。不仅如此,Class 类还提供了很多使用 RTTI 的其它方式。

类是程序的一部分,每个类都有一个 Class 对象。换言之,每当我们编写并且编译了一个新类,就会产生一个 Class 对象(更恰当的说,是被保存在一个同名的 .class 文件中)。为了生成这个类的对象,Java 虚拟机 (JVM) 先会调用"类加载器"子系统把这个类加载到内存中。

类加载器子系统可能包含一条类加载器链,但有且只有一个原生类加载器,它是 JVM 实现的一部分。原生类加载器加载的是”可信类”(包括 Java API 类)。它们通常是从本地盘加载的。在这条链中,通常不需要添加额外的类加载器,但是如果你有特殊需求(例如以某种特殊的方式加载类,以支持 Web 服务器应用,或者通过网络下载类),也可以挂载额外的类加载器。

所有的类都是第一次使用时动态加载到 JVM 中的,当程序创建第一个对类的静态成员的引用时,就会加载这个类。

其实构造器也是类的静态方法,虽然构造器前面并没有 static 关键字。所以,使用 new 操作符创建类的新对象,这个操作也算作对类的静态成员引用。

因此,Java 程序在它开始运行之前并没有被完全加载,很多部分是在需要时才会加载。这一点与许多传统编程语言不同,动态加载使得 Java 具有一些静态加载语言(如 C++)很难或者根本不可能实现的特性。

类加载器首先会检查这个类的 Class 对象是否已经加载,如果尚未加载,默认的类加载器就会根据类名查找 .class 文件(如果有附加的类加载器,这时候可能就会在数据库中或者通过其它方式获得字节码)。这个类的字节码被加载后,JVM 会对其进行验证,确保它没有损坏,并且不包含不良的 Java 代码(这是 Java 安全防范的一种措施)。

一旦某个类的 Class 对象被载入内存,它就可以用来创建这个类的所有对象。下面的示范程序可以证明这点:

// typeinfo/SweetShop.java
// 检查类加载器工作方式
class Cookie {
    static {
        System.out.println("Loading Cookie");
    }
}

class Gum {
    static {
        System.out.println("Loading Gum");
    }
}

class Candy {
    static {
        System.out.println("Loading Candy");
    }
}

public class SweetShop {
    public static void main(String[] args) {
        System.out.println("inside main");
        new Candy();
        System.out.println("After creating Candy");
        try {
            Class.forName("Gum");
        } catch (ClassNotFoundException e) {
            System.out.println("Couldn't find Gum");
        }
        System.out.println("After Class.forName(\"Gum\")");
        new Cookie();
        System.out.println("After creating Cookie");
    }
}

输出结果:

在这里插入图片描述

上面的代码中,CandyGumCookie 这几个类都有一个 static{...} 静态初始化块,这些静态初始化块在类第一次被加载的时候就会执行。也就是说,静态初始化块会打印出相应的信息,告诉我们这些类分别是什么时候被加载了。而在主方法里边,创建对象的代码都放在了 print() 语句之间,以帮助我们判断类加载的时间点。

从输出中可以看到,Class 对象仅在需要的时候才会被加载,static 初始化是在类加载时进行的。

代码里面还有特别有趣的一行:

Class.forName("Gum");

所有 Class 对象都属于 Class 类,而且它跟其他普通对象一样,我们可以获取和操控它的引用(这也是类加载器的工作)。forName()Class 类的一个静态方法,我们可以使用 forName() 根据目标类的类名(String)得到该类的 Class 对象。上面的代码忽略了 forName() 的返回值,因为那个调用是为了得到它产生的“副作用”。从结果可以看出,forName() 执行的副作用是如果 Gum 类没有被加载就加载它,而在加载的过程中,Gumstatic 初始化块被执行了。

还需要注意的是,如果 Class.forName() 找不到要加载的类,它就会抛出异常 ClassNotFoundException。上面的例子中我们只是简单地报告了问题,但在更严密的程序里,就要考虑在异常处理程序中把问题解决掉。

无论何时,只要你想在运行时使用类型信息,就必须先得到那个 Class 对象的引用。Class.forName() 就是实现这个功能的一个便捷途径,因为使用该方法你不需要先持有这个类型 的对象。但是,如果你已经拥有了目标类的对象,那就可以通过调用 getClass() 方法来获取 Class 引用了,这个方法来自根类 Object,它将返回表示该对象实际类型的 Class 对象的引用。Class 包含很多有用的方法,下面代码展示了其中的一部分:

interface HasBatteries {
}

interface Waterproof {
}

interface Shoots {
}

class Toy {
    // 注释下面的无参数构造器会引起 NoSuchMethodError 错误
    Toy() {
    }

    Toy(int i) {
    }
}

class FancyToy extends Toy
        implements HasBatteries, Waterproof, Shoots {
    FancyToy() {
        super(1);
    }
}

public class ToyTest {
    static void printInfo(Class cc) {
        System.out.println("Class name: " + cc.getName() +
                " is interface? [" + cc.isInterface() + "]");
        System.out.println(
                "Simple name: " + cc.getSimpleName());
        System.out.println(
                "Canonical name : " + cc.getCanonicalName());
    }

    public static void main(String[] args) {
        Class c = null;
        try {
            c = Class.forName("com.example.test.ToyTest");
        } catch (ClassNotFoundException e) {
            System.out.println("Can't find FancyToy");
            System.exit(1);
        }

        printInfo(c);
        for (Class face : c.getInterfaces()) {
            printInfo(face);
        }

        Class up = c.getSuperclass();
        Object obj = null;

        try {
            // Requires no-arg constructor:
            obj = up.newInstance();
        } catch (InstantiationException e) {
            System.out.println("Cannot instantiate");
            System.exit(1);
        } catch (IllegalAccessException e) {
            System.out.println("Cannot access");
            System.exit(1);
        }

        printInfo(obj.getClass());
    }
}

输出结果:

在这里插入图片描述

FancyToy 继承自 Toy 并实现了 HasBatteriesWaterproofShoots 接口。在 main 方法中,我们创建了一个 Class 引用,然后在 try 语句里边用 forName() 方法创建了一个 FancyToy 的类对象并赋值给该引用。需要注意的是,传递给 forName() 的字符串必须使用类的全限定名(包含包名)。

printInfo() 函数使用 getName() 来产生完整类名,使用 getSimpleName() 产生不带包名的类名,getCanonicalName() 也是产生完整类名(除内部类和数组外,对大部分类产生的结果与 getName() 相同)。isInterface() 用于判断某个 Class 对象代表的是否为一个接口。因此,通过 Class 对象,你可以得到关于该类型的所有信息。

在主方法中调用的 Class.getInterfaces() 方法返回的是存放 Class 对象的数组,里面的 Class 对象表示的是那个类实现的接口。

另外,你还可以调用 getSuperclass() 方法来得到父类的 Class 对象,再用父类的 Class 对象调用该方法,重复多次,你就可以得到一个对象完整的类继承结构。

Class 对象的 newInstance() 方法是实现“虚拟构造器”的一种途径,虚拟构造器可以让你在不知道一个类的确切类型的时候,创建这个类的对象。在前面的例子中,up 只是一个 Class 对象的引用,在编译期并不知道这个引用会指向哪个类的 Class 对象。当你创建新实例时,会得到一个 Object 引用,但是这个引用指向的是 Toy 对象。当然,由于得到的是 Object 引用,目前你只能给它发送 Object 对象能够接受的调用。而如果你想请求具体对象才有的调用,你就得先获取该对象更多的类型信息,并执行某种转型。另外,使用 newInstance() 来创建的类,必须带有无参数的构造器。在本章稍后部分,你将会看到如何通过 Java 的反射 API,用任意的构造器来动态地创建类的对象。

类字面常量

Java 还提供了另一种方法来生成类对象的引用:类字面常量。对上述程序来说,就像这样:FancyToy.class;。这样做不仅更简单,而且更安全,因为它在编译时就会受到检查(因此不必放在 try 语句块中)。并且它根除了对 forName() 方法的调用,所以效率更高。

类字面常量不仅可以应用于普通类,也可以应用于接口、数组以及基本数据类型。另外,对于基本数据类型的包装类,还有一个标准字段 TYPETYPE 字段是一个引用,指向对应的基本数据类型的 Class 对象,如下所示:

…等价于…
boolean.classBoolean.TYPE
char.classCharacter.TYPE
byte.classByte.TYPE
short.classShort.TYPE
int.classInteger.TYPE
long.classLong.TYPE
float.classFloat.TYPE
double.classDouble.TYPE
void.classVoid.TYPE

我的建议是使用 .class 的形式,以保持与普通类的一致性。

注意,有一点很有趣:当使用 .class 来创建对 Class 对象的引用时,不会自动地初始化该 Class 对象。为了使用类而做的准备工作实际包含三个步骤:

  1. 加载,这是由类加载器执行的。该步骤将查找字节码(通常在 classpath 所指定的路径中查找,但这并非是必须的),并从这些字节码中创建一个 Class 对象。
  2. 链接。在链接阶段将验证类中的字节码,为 static 字段分配存储空间,并且如果需要的话,将解析这个类创建的对其他类的所有引用。
  3. 初始化。如果该类具有超类,则先初始化超类,执行 static 初始化器和 static 初始化块。

直到第一次引用一个 static 方法(构造器隐式地是 static)或者非常量的 static 字段,才会进行类初始化。

import java.util.*;

class Initable {
    static final int STATIC_FINAL = 47;
    static final int STATIC_FINAL2 =
            ClassInitialization.rand.nextInt(1000);

    static {
        System.out.println("Initializing Initable");
    }
}

class Initable2 {
    static int staticNonFinal = 147;

    static {
        System.out.println("Initializing Initable2");
    }
}

class Initable3 {
    static int staticNonFinal = 74;

    static {
        System.out.println("Initializing Initable3");
    }
}

public class ClassInitialization {
    public static Random rand = new Random(47);

    public static void
    main(String[] args) throws Exception {
        Class initable = Initable.class;
        System.out.println("After creating Initable ref");
        // Does not trigger initialization:
        System.out.println(Initable.STATIC_FINAL);
        // Does trigger initialization:
        System.out.println(Initable.STATIC_FINAL2);
        // Does trigger initialization:
        System.out.println(Initable2.staticNonFinal);
        Class initable3 = Class.forName("com.example.test.Initable3");
        System.out.println("After creating Initable3 ref");
        System.out.println(Initable3.staticNonFinal);
    }
}

输出结果:

在这里插入图片描述

初始化有效地实现了尽可能的“惰性”,从对 initable 引用的创建中可以看到,仅使用 .class 语法来获得对类对象的引用不会引发初始化。但与此相反,使用 Class.forName() 来产生 Class 引用会立即就进行初始化,如 initable3

如果一个 static final 值是“编译期常量”(如 Initable.staticFinal),那么这个值不需要对 Initable 类进行初始化就可以被读取。但是,如果只是将一个字段设置成为 staticfinal,还不足以确保这种行为。例如,对 Initable.staticFinal2 的访问将强制进行类的初始化,因为它不是一个编译期常量。

如果一个 static 字段不是 final 的,那么在对它访问时,总是要求在它被读取之前,要先进行链接(为这个字段分配存储空间)和初始化(初始化该存储空间),就像在对 Initable2.staticNonFinal 的访问中所看到的那样。

泛化的 Class 引用

Class 引用总是指向某个 Class 对象,而 Class 对象可以用于产生类的实例,并且包含可作用于这些实例的所有方法代码。它还包含该类的 static 成员,因此 Class 引用表明了它所指向对象的确切类型,而该对象便是 Class 类的一个对象。
但是,Java 设计者看准机会,将它的类型变得更具体了一些。Java 引入泛型语法之后,我们可以使用泛型对 Class 引用所指向的 Class 对象的类型进行限定。在下面的实例中,两种语法都是正确的:

// typeinfo/GenericClassReferences.java

public class GenericClassReferences {
    public static void main(String[] args) {
        Class intClass = int.class;
        Class<Integer> genericIntClass = int.class;
        genericIntClass = Integer.class; // 同一个东西
        intClass = double.class;
        // genericIntClass = double.class; // 非法
    }
}

普通的类引用不会产生警告信息。你可以看到,普通的类引用可以重新赋值指向任何其他的 Class 对象,但是使用泛型限定的类引用只能指向其声明的类型。通过使用泛型语法,我们可以让编译器强制执行额外的类型检查。

那如果我们希望稍微放松一些限制,应该怎么办呢?乍一看,下面的操作好像是可以的:

Class<Number> geenericNumberClass = int.class;

这看起来似乎是起作用的,因为 Integer 继承自 Number。但事实却是不行,因为 IntegerClass 对象并不是 NumberClass 对象的子类。

为了在使用 Class 引用时放松限制,我们使用了通配符,它是 Java 泛型中的一部分。通配符就是 ?,表示“任何事物”。因此,我们可以在上例的普通 Class 引用中添加通配符,并产生相同的结果:

// typeinfo/WildcardClassReferences.java

public class WildcardClassReferences {
    public static void main(String[] args) {
        Class<?> intClass = int.class;
        intClass = double.class;
    }
}

使用 Class<?> 比单纯使用 Class 要好,虽然它们是等价的,并且单纯使用 Class 不会产生编译器警告信息。使用 Class<?> 的好处是它表示你并非是碰巧或者由于疏忽才使用了一个非具体的类引用,而是特意为之。

为了创建一个限定指向某种类型或其子类的 Class 引用,我们需要将通配符与 extends 关键字配合使用,创建一个范围限定。这与仅仅声明 Class<Number> 不同,现在做如下声明:

// typeinfo/BoundedClassReferences.java

public class BoundedClassReferences {
    public static void main(String[] args) {
        Class<? extends Number> bounded = int.class;
        bounded = double.class;
        bounded = Number.class;
        // Or anything else derived from Number.
    }
}

Class 引用添加泛型语法的原因只是为了提供编译期类型检查,因此如果你操作有误,稍后就会发现这点。使用普通的 Class 引用你要确保自己不会犯错,因为一旦你犯了错误,就要等到运行时才能发现它,很不方便。

下面的示例使用了泛型语法,它保存了一个类引用,稍后又用 newInstance() 方法产生类的对象:

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

class CountedInteger {
    private static long counter;
    private final long id = counter++;

    @Override
    public String toString() {
        return Long.toString(id);
    }
}

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

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

    @Override
    public T get() {
        try {
            return type.newInstance();
        } catch (InstantiationException | IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }

    public static void main(String[] args) {
        Stream.generate(new DynamicSupplier<>(CountedInteger.class))
                .skip(10)
                .limit(5)
                .forEach(System.out::println);
    }
}

输出结果:

在这里插入图片描述

注意,这个类必须假设与它一起工作的任何类型都有一个无参构造器,否则运行时会抛出异常。编译期对该程序不会产生任何警告信息。

当你将泛型语法用于 Class 对象时,newInstance() 将返回该对象的确切类型,而不仅仅只是在 ToyTest.java 中看到的基类 Object。然而,这在某种程度上有些受限:

public class GenericToyTest {
    public static void
    main(String[] args) throws Exception {
        Class<FancyToy> ftClass = FancyToy.class;
        // Produces exact type:
        FancyToy fancyToy = ftClass.newInstance();
        Class<? super FancyToy> up = ftClass.getSuperclass();
        // This won't compile:
        // Class<Toy> up2 = ftClass.getSuperclass();
        // Only produces Object:
        Object obj = up.newInstance();
    }
}

如果你手头的是超类,那编译器将只允许你声明超类引用为“某个类,它是 FancyToy 的超类”,就像在表达式 Class<? super FancyToy> 中所看到的那样。而不会接收 Class<Toy> 这样的声明。这看上去显得有些怪,因为 getSuperClass() 方法返回的是基类(不是接口),并且编译器在编译期就知道它是什么类型了(在本例中就是 Toy.class),而不仅仅只是"某个类"。不管怎样,正是由于这种含糊性,up.newInstance 的返回值不是精确类型,而只是 Object

cast() 方法

Java 中还有用于 Class 引用的转型语法,即 cast() 方法:

// typeinfo/ClassCasts.java

class Building {}
class House extends Building {}

public class ClassCasts {
    public static void main(String[] args) {
        Building b = new House();
        Class<House> houseType = House.class;
        House h = houseType.cast(b);
        h = (House)b; // ... 或者这样做.
    }
}

cast() 方法接受参数对象,并将其类型转换为 Class 引用的类型。但是,如果观察上面的代码,你就会发现,与实现了相同功能的 main 方法中最后一行相比,这种转型好像做了很多额外的工作。

cast() 在无法使用普通类型转换的情况下会显得非常有用,在你编写泛型代码时,如果你保存了 Class 引用,并希望以后通过这个引用来执行转型,你就需要用到 cast()。但事实却是这种情况非常少见,我发现整个 Java 类库中,只有一处使用了 cast()(在 com.sun.mirror.util.DeclarationFilter 中)。

Java 类库中另一个没有任何用处的特性就是 Class.asSubclass(),该方法允许你将一个 Class 对象转型为更加具体的类型。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

JVM第二十三讲:Java动态调试技术原理

Java动态调试技术原理 本文是JVM第二十三讲&#xff0c;Java动态调试技术原理。转载自 美团技术团队胡健的Java 动态调试技术原理及实践&#xff0c;通过学习java agent方式进行动态调试&#xff0c;了解目前很多大厂开源的一些基于此的调试工具 (例如来自阿里开源的Arthas)。 …

微信小程序设计之主体文件app-wxss/less

一、新建一个项目 首先&#xff0c;下载微信小程序开发工具&#xff0c;具体下载方式可以参考文章《微信小程序开发者工具下载》。 然后&#xff0c;注册小程序账号&#xff0c;具体注册方法&#xff0c;可以参考文章《微信小程序个人账号申请和配置详细教程》。 在得到了测…

elementUI 特定分辨率(如1920*1080)下el-row未超出一行却换行

在1920*1080分辨率下&#xff0c; el-col 内容未超出 el-col 宽度&#xff0c;el-col 不足以占据一行&#xff0c;el-row 却自动换行了&#xff08;其他分辨率没有这个问题&#xff09;。 截图&#xff1a; 排查&#xff1a; el-col 内容没有溢出&#xff1b;没有多余的 pad…

拜耳阵列(Bayer Pattern)和解马赛克简介

拜尔阵列 典型的图像传感器&#xff08;例如我们在数码相机中使用的图像传感器&#xff0c;主要有CCD, CMOS&#xff09;由许多单独的光电传感器组成&#xff0c;所有这些传感器都会捕获光线。这些光电传感器本身能够捕获光的强度&#xff0c;但不能捕获其波长&#xff08;颜色…

CTF-Web(3)文件上传漏洞

笔记目录 CTF-Web(2)SQL注入CTF-Web(3)文件上传漏洞 1.WebShell介绍 (1)一句话木马定义 一种网页后门&#xff0c;以asp、php、jsp等网页文件形式存在的一种命令执行环境&#xff0c;而 一句话木马往往只有一行WebShell代码。 作用&#xff1a; 攻击获得网站控制权限 查看、修改…

如何防范AI等技术带来的诈骗风险?从技术、法律、教育等多方面入手

文章目录 前言什么是AI诈骗案例案例一案例二 AI诈骗的特点如何预防和应对AI诈骗建议后记 前言 互联网是一把双刃剑&#xff0c;这是我们常说的一个问题。 随着人工智能技术的快速发展&#xff0c;AI诈骗成为当今社会面临的新兴威胁。不法分子利用人工智能技术&#xff0c;以更…

Qt之实现支持多选的QCombobox

一.效果 1.点击下拉列表的复选框区域 2.点击下拉列表的非复选框区域 二.实现 QHCustomComboBox.h #ifndef QHCUSTOMCOMBOBOX_H #define QHCUSTOMCOMBOBOX_H#include <QLineEdit> #include <QListWidget> #include <QCheckBox> #include <QComboBox>…

面试算法43:在完全二叉树中添加节点

题目 在完全二叉树中&#xff0c;除最后一层之外其他层的节点都是满的&#xff08;第n层有2n-1个节点&#xff09;。最后一层的节点可能不满&#xff0c;该层所有的节点尽可能向左边靠拢。例如&#xff0c;图7.3中的4棵二叉树均为完全二叉树。实现数据结构CBTInserter有如下3种…

Vue 3 响应式对象:ref 和 reactive 的使用和区别

&#x1f389;&#x1f389;欢迎来到我的CSDN主页&#xff01;&#x1f389;&#x1f389; &#x1f3c5;我是尘缘&#xff0c;一个在CSDN分享笔记的博主。&#x1f4da;&#x1f4da; &#x1f449;点击这里&#xff0c;就可以查看我的主页啦&#xff01;&#x1f447;&#x…

Flink CDC 2.0 主要是借鉴 DBLog 算法

DBLog 算法原理 DBLog 这个算法的原理分成两个部分&#xff0c;第一部分是分 chunk&#xff0c;第二部分是读 chunk。分 chunk 就是把一张表分为多个 chunk&#xff08;桶/片&#xff09;。我可以把这些 chunk 分发给不同的并发的 task 去做。例如&#xff1a;有 reader1 和 re…

二叉树的最近公共祖先

题目&#xff1a; 样例&#xff1a; 输入 6 1 4 2 5 -1 -1 1 4 -1 -1 -1 -1 -1 3 输出 2 思路&#xff1a; 由题意&#xff0c;最近公共祖先就是&#xff0c;找出给出的两个结点的父结点 是谁。 这里有两种情况 1、给定的两个结点都是孩子结点 2、给定的两个结点&#xff…

【送书福利-第二十二期】《Vue.js 3企业级项目开发实战(微课视频版)》

&#x1f60e; 作者介绍&#xff1a;我是程序员洲洲&#xff0c;一个热爱写作的非著名程序员。CSDN全栈优质领域创作者、华为云博客社区云享专家、阿里云博客社区专家博主、前后端开发、人工智能研究生。公粽号&#xff1a;程序员洲洲。 &#x1f388; 本文专栏&#xff1a;本文…

Table-GPT:让大语言模型理解表格数据

llm对文本指令非常有用&#xff0c;但是如果我们尝试向模型提供某种文本格式的表格数据和该表格上的问题&#xff0c;LLM更有可能产生不准确的响应。 在这篇文章中&#xff0c;我们将介绍微软发表的一篇研究论文&#xff0c;“Table-GPT: Table- tuning GPT for Diverse Table…

用示例和应用程序了解必要的Golang库

Golang&#xff0c;也被称为Go&#xff0c;因其简单性、性能和并发性支持而在开发人员中迅速流行起来。导致Go成功的关键因素之一是其丰富的库生态系统&#xff0c;可以简化开发并提供解决常见问题的解决方案。在本文中&#xff0c;我们将更仔细地查看一些必要的Golang库&#…

心血管疾病药物不良反应不容忽视,华大基因基因检测辅助降低风险!

随着医疗技术的不断进步&#xff0c;个体化用药已经成为药物治疗的新趋势。在此趋势下&#xff0c;华大基因基因检测基于药物基因组学的药物选择和个性化用药方案&#xff0c;为心血管疾病患者的临床治疗提供了新机会&#xff0c;同时可以更好地帮助患者控制心血管疾病&#xf…

数据结构之栈的讲解(源代码+图解+习题)

我们在学习过顺序表和链表之后&#xff0c;了解了使用数组存储数据&#xff0c;使用结构体来存储数据和有关的指针&#xff0c;这些都是底层的东西&#xff0c;链表是靠指针的链接&#xff0c;顺序表是靠数组的下标才能得以实现增删查改。众多数据结构其实底层都离不开数组&…

HTML简单实现v-if与v-for与v-model

Vue启动&#xff01;&#xff01; 首先VIewModel将View和Model连接一起&#xff0c;Model的数据改变View的数据也变 使用Visual Studio Code 启动Vue需要vue.js插件和导入CDN(包) vue.js插件&#xff1a;CTRL shift x 在搜索栏搜 索vue.js安装即可 CDN&#xff1a; http…

使用Terraform管理已经存在的kubernates和默认的节点池

背景&#xff1a; 通过terraform resource "alicloud_cs_managed_kubernetes" "k8s" {...}创建集群时&#xff0c;会产生一个默认的节点池default-nodepool&#xff0c;但是如何去修改这个默认节点池的信息呢&#xff1f; 解决思路&#xff1a; 因为Ter…

2021美亚个人赛复现1

Individual_Container.zip.001下载以后显示是一个压缩包格式&#xff08;解压密码&#xff1a;MeiyaCup2021&#xff09; 解压得到Individual_Container加密容器&#xff0c;赛题存储在这里面 挂载密码HfsCk]<eUqc5Q{(DG$ugiGlt8ezGdaZ>!pQC-H\5BAc^gBo/^qq)/i21ufiNH&…

TELUS Ventures(泰勒斯)

TELUS Ventures&#xff08;泰勒斯&#xff09;高峰论坛于2023年10月28日在南京第5站正式开幕。该论坛是由泰勒斯风险投资公司主办的一项重要活动&#xff0c;旨在促进创新和创业精神的发展 。 这次高峰论坛将汇集来自全球各地的创业者、投资者和行业专家&#xff0c;共同探讨…