Java反射与“整活--(IOC容器)”

文章目录

  • 前言
  • 反射
    • 什么是反射
    • 基本操作
      • 获取类对象
      • 获取类属性
      • 获取类方法
      • 方法的执行
      • 对构造方法的操作
  • 注解
    • 定义
    • 获取注解
  • 整活(IOC容器)
    • 项目结构
    • IOC/DI流程
      • ApplicationContext
      • BeanDefinitionReader
      • BeanDefinition
      • BeanWrapper
      • getBean()方法
  • IOC完整实现
    • 启动/调用
    • 完整实现

前言

没啥意思,太无无聊了,中值定理玩到吐,最近在复习高数,考研和开发并发进行,恢复博文更新,一方面是为了毕设,另一方面是为了秋招,看看有没有机会。当然考研还是个大方向,但是如果有合适的机会,那么,实话实话,鄙人没有什么远大理想,就搞钱钱,如果去读个研,后面还得装sz拿着可怜,甚至没有的补助的话,那不如去上个班。书什么时候都可以去读,但是机会不见得什么时候都有。至于所谓的人脉,非头部,别说话。那么废话不多说,我们来玩玩今天的反射吧。之后,我们再看看,如何将我们的反射用到我们的实际的项目当中,而不是,一直使用Spring或者SpringBoot。这里我们是做Java程序员,不是Spring
\SpringBoot程序员。你可以说,你常用的玩意是它,但是不能说你只会这玩意。

反射

什么是反射

Java反射是指在运行时动态地获取类的信息并操作对象的能力。通过反射,可以在运行时检查和修改类、方法、字段等的属性和行为,即使在编译时无法确定具体的类结构和方法。

举个例子:假设你拿到了一本书,但是在不打开书的情况下,你只能看到书的封面,而无法知道书中具体的内容。此时,反射就相当于打开这本书,你可以逐页阅读书籍的每个章节、段落和文字,甚至可以修改或添加内容。通过反射,你可以探索和操作类的结构和对象的特性,灵活地进行编程。
Java反射就是提供了一种动态检查和操作类、方法、字段等的能力。 通过反射,我们可以获取到类的属性,方法等等。通过这些内容,我们可以很轻松地完成类的操作。并且可以不进行显示的new对象。方便进行扩展,等操作。

基本操作

概念的话,我就念到这里,说实话,作用不大,直接看代码作用更大。

获取类对象

在Java中,可以通过以下三种方式来获取类对象:

  1. 使用Class.forName()方法:
    这是一种常用的方式,通过提供类的全限定名作为参数,返回对应的Class对象。例如:

    Class<?> clazz = Class.forName("com.example.MyClass");
    
  2. 使用类字面常量:
    可以直接使用类字面常量来获取类对象,这是一种更加简洁方便的方式。例如:

    Class<?> clazz = MyClass.class;
    
  3. 调用对象的getClass()方法:
    在已经有一个对象的情况下,可以通过调用该对象的getClass()方法来获取类对象。例如:

    MyClass obj = new MyClass();
    Class<?> clazz = obj.getClass();
    

获取类属性

要获取类的属性,可以使用以下方法:

  1. 使用Class对象的getFields()方法:
    该方法返回类的所有公共字段(包括继承的字段),并以Field对象数组的形式返回。例如:

    Class<?> clazz = MyClass.class;
    Field[] fields = clazz.getFields();
    
  2. 使用Class对象的getField(String name)方法:
    通过指定字段名称,可以获取类的特定公共字段。如果字段不存在或不可访问,则会抛出NoSuchFieldException异常。例如:

    Class<?> clazz = MyClass.class;
    Field field = clazz.getField("fieldName");
    
  3. 使用Class对象的getDeclaredFields()方法:
    该方法返回类的所有字段(不包括继承的字段),并以Field对象数组的形式返回。例如:

    Class<?> clazz = MyClass.class;
    Field[] fields = clazz.getDeclaredFields();
    
  4. 使用Class对象的getDeclaredField(String name)方法:
    通过指定字段名称,可以获取类的特定字段,无论其访问权限如何。如果字段不存在,则会抛出NoSuchFieldException异常。例如:

    Class<?> clazz = MyClass.class;
    Field field = clazz.getDeclaredField("fieldName");
    

这些方法提供了不同的方式来获取类的属性信息,包括公共字段和私有字段。需要注意的是,不过对于私有字段,需要通过setAccessible(true)方法设置访问权限,才能对其进行操作。此外,还可以通过Field对象的getName()、getType()等方法获取字段的名称、类型等相关信息。

此外我们还可以通过如下方法来对类的属性进行操作:

使用Field对象的set(Object obj, Object value)方法:
首先需要获取对应的Field对象,然后使用set方法设置属性的值。第一个参数是要设置属性值的对象实例,如果是静态字段,则可以传入null;第二个参数是要设置的属性值。例如:

Class<?> clazz = MyClass.class;
Field field = clazz.getDeclaredField("fieldName");
field.setAccessible(true); // 设置访问权限
Object obj = new MyClass();
field.set(obj, value); // 设置属性值

此外还要使用Field对象的setInt(Object obj, int value)、setDouble(Object obj, double value)等类型特定的方法,无法返回对象是你特定的,而不是Object罢了。

获取类方法

之后是获取到类的方法

  1. 使用Class对象的getMethods()方法:
    该方法返回类的所有公共方法(包括继承的方法),并以Method对象数组的形式返回。例如:

    Class<?> clazz = MyClass.class;
    Method[] methods = clazz.getMethods();
    
  2. 使用Class对象的getMethod(String name, Class<?>… parameterTypes)方法:
    通过指定方法名称和参数类型,可以获取类的特定公共方法。如果方法不存在或不可访问,则会抛出NoSuchMethodException异常。例如:

    Class<?> clazz = MyClass.class;
    Method method = clazz.getMethod("methodName", String.class, int.class);
    
  3. 使用Class对象的getDeclaredMethods()方法:
    该方法返回类的所有方法(不包括继承的方法),并以Method对象数组的形式返回。例如:

    Class<?> clazz = MyClass.class;
    Method[] methods = clazz.getDeclaredMethods();
    
  4. 使用Class对象的getDeclaredMethod(String name, Class<?>… parameterTypes)方法:
    通过指定方法名称和参数类型,可以获取类的特定方法,无论其访问权限如何。如果方法不存在,则会抛出NoSuchMethodException异常。例如:

    Class<?> clazz = MyClass.class;
    Method method = clazz.getDeclaredMethod("methodName", String.class, int.class);
    

这些方法提供了不同的方式来获取类的方法信息,包括公共方法和私有方法。需要注意的是,对于私有方法,需要通过setAccessible(true)方法设置访问权限,才能对其进行调用。此外,还可以通过Method对象的getName()、getReturnType()等方法获取方法的名称、返回类型等相关信息。

方法的执行

现在,我们获取到了这个方法还不够,我们还需要进行执行到这个方法,不然获取这个玩意有啥。

我们从头捋一遍:

  1. 获取方法对象:
    首先,需要获取目标类的Class对象,然后使用getMethod()getDeclaredMethod()方法获得特定的方法对象。例如:

    Class<?> clazz = MyClass.class;
    Method method = clazz.getDeclaredMethod("methodName", String.class, int.class);
    
  2. 获取方法的输入参数信息:
    使用方法对象的getParameterTypes()方法获取方法的参数类型数组。例如:

    Class<?>[] parameterTypes = method.getParameterTypes();
    
  3. 构造方法的参数值:
    根据参数类型,构造一个与方法参数一一对应的参数值数组。例如:

    Object[] arguments = new Object[parameterTypes.length];
    arguments[0] = "example";
    arguments[1] = 10;
    
  4. 设置私有方法可访问:
    如果是私有方法,需要设置方法的可访问性:

    method.setAccessible(true);
    
  5. 执行方法并获取返回值:
    使用方法对象的invoke()方法执行方法,并获取返回值。例如:

    Object result = method.invoke(obj, arguments);
    

对构造方法的操作

这个没啥,就是用这个玩意来newInstance()一个对象,就没了。
其他的作用不大,就玩玩这些对象就够用了。

注解

说到了,这个反射,那肯定是不够的,俺们还有注解没有说。

注解(Annotation)是Java语言提供的一种元数据(Metadata)机制,它可以用于在类、方法、字段等程序元素上添加额外的信息。注解可以在编译时和运行时被读取和处理,用于对代码进行标记、描述或配置。

定义

Java中的注解使用@符号来标识,放置在目标元素前面。例如,常见的注解包括:

  1. @Override:用于标识方法覆盖父类的方法。
  2. @Deprecated:用于标识过时的方法或类。
  3. @SuppressWarnings:用于抑制编译器警告。
  4. @Entity:用于标识JPA实体类。
  5. @RequestMapping:用于标识Spring MVC控制器方法的映射路径。

此外,我们还可以通过@interface关键字来定义一个新的注解类型。例如:

import java.lang.annotation.*;@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyAnnotation {String value();int count() default 0;
}

上述代码定义了一个名为MyAnnotation的自定义注解,包含了一个value属性和一个count属性。注解的生命周期为运行时,并且只能应用于方法上。

此外,对于注解,我们还有对注解的一些描述,这些描述,是对注解进行的一些说明,描述,并给JVM进程一定的操作。例如这个注解作用到哪里,什么时候执行等等。这些被称为元注解。
例如:

import java.lang.annotation.*;@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)

以下是Java中常用的元注解:

  1. @Retention:用于指定注解的生命周期,包括三个取值:

    • RetentionPolicy.SOURCE:注解仅存在于源代码中,在编译时会被忽略。
    • RetentionPolicy.CLASS:注解会被保留在编译后的字节码文件中,但在运行时不可用。
    • RetentionPolicy.RUNTIME:注解会被保留在编译后的字节码文件中,并在运行时可以通过反射读取。
  2. @Target:用于指定注解适用的目标元素类型,包括多个取值:

    • ElementType.TYPE:类、接口、枚举等。
    • ElementType.FIELD:字段、枚举常量。
    • ElementType.METHOD:方法。
    • ElementType.PARAMETER:方法参数。
    • ElementType.CONSTRUCTOR:构造函数。
    • ElementType.LOCAL_VARIABLE:局部变量。
    • ElementType.ANNOTATION_TYPE:注解类型。
    • ElementType.PACKAGE:包。
    • ElementType.TYPE_PARAMETER:泛型参数。
    • ElementType.TYPE_USE:类型使用。
  3. @Documented:用于指定注解是否包含在Java文档中。

  4. @Inherited:用于指定注解是否可以被继承。

获取注解

通过,这个玩意,我们可以获取到类上面的注解

 Annotation[] annotations = clazz.getAnnotations();

此外,我们还可以获取值:
这是一段示例

import java.lang.annotation.Annotation;@MyAnnotation(name = "MyClass", version = 1.0)
public class MyClass {public static void main(String[] args) {Class<?> clazz = MyClass.class;// 获取类的注解Annotation[] annotations = clazz.getAnnotations();for (Annotation annotation : annotations) {if (annotation instanceof MyAnnotation) {MyAnnotation myAnnotation = (MyAnnotation) annotation;String name = myAnnotation.name();double version = myAnnotation.version();System.out.println("Class Name: " + name);System.out.println("Version: " + version);}}}
}

当然除此之外,我们还可以获取方法上面的。
其实是一样的:

Annotation[] annotations = method.getAnnotations();

其他啥也没变。

整活(IOC容器)

现在基础概念和使用方法,过了一遍了,那么我们接下来就要进行简单的运用了,如何把这个玩意用在我们实际的项目开发当中。那么接下来我们来编写一个,简单的项目架子。以后在些一些非Web应用的时候也用上来。虽然简陋,但是性能绝对很牛逼(因为简单,用安全性换的)。

来,先看到,俺们的项目,啥也没有:
在这里插入图片描述
在未来,这个项目将变得非常复杂,就像这样:
在这里插入图片描述
并且在这些玩意里面有非常多的东西需要处理。不过在这里,不管这个项目有多复杂。有一个亘古不变的东西,那就是,这些代码分为两大阵营,第一大阵营是苦力工,专门负责运送,加工数据。另一大阵营是数据载体,专门负责运载数据。也就是各种Service,Implement,和Dao,Entity,只不过这些苦力有各种各样的工种,有些负责和数据库打交道,有些负责和其他第三方or rpc接口打交道。同样的对于数据载体也是不同的,有很多类别

对于数据载体来说,每一个载体都是独一无二的,因为,运载的数据不同。但是一个苦力却可以一直不断地重复处理他所对应的数据。这是啥意思咧,每次我来了一个新的数据,需要处理的时候,我需要找个苦力来进行处理,比如,有一个数据负责用户的注册,我此时就需要一个员工来处理这件事情,并且从事UserService有3年工作检验,来帮我把这个数据,运送到叫做Mysql的地方去,有些有钱的老板还会放到叫做Oracle的地方。不过大部分的老板都没钱,于是只能放到叫做Mysql的地方。当然有时候也会放到redis,mongdb,kafka,es, 等等地方去。这个时候,我有两个选择,要么当数据运算完毕之后,就开除这个员工,要么,先把这个员工招进来,等我需要处理这个数据的时候,就叫他来。如果长时间业务不好,那就再裁掉这个员工。由于每次都招的话,实在是太慢了,于是你选择养起来。招聘员工,先养着,后来随着业务不断扩大,你管理起来越发麻烦了,于是你成立了一个叫做人事部门,当你需要谁去搬砖的时候,就通知人事,叫他过去就好了,当业务不好的时候,再通知人事裁员就好了,最后再把人事裁了。

那么这个小案例,就说明到了,我们要整活的玩意,就是做一个简单的IOC容器,然后实现控制反转。

项目结构

在这里插入图片描述
其实我们的核心很简单,就是希望通过ApplicationContext 这个玩意可以帮助我拿到类。其他的没啥了,然后这个玩意做为路口,负责我们整个项目的处理,就这么简单。

那么怎么完成管理呢,其实最简单的办法其实就是,把对应的信息放在Map里面不就好了。

IOC/DI流程

在这里插入图片描述

ApplicationContext

这个呢,是咱们整个IOC的入口,其实在Spring里面有一个BeanFactory这个是顶层接口,然后那个ApplicationContext是那个接口的实现。这里没有搞那么复杂,不过流程大体流程是这样子的。

这个ApplicationContext提供了一个非常重要的方法,就是getBean()方法。这个方法就可以帮我们把那个容器里面的对象拿出来。

BeanDefinitionReader

这个是一个解析器嘛,负责解析各种配置文件,比如xml,yaml,properites之类的。
解析之后,要做的是把这个配置文件对应的内容给解析出来,然后把东西封装到BeanDefinition里面

BeanDefinition

这个玩意呢,是封装的我们的类的(被扫描出来)的信息。比如
我们在配置文件里面写的玩意是
在这里插入图片描述
我们要扫描这个包下面的所有类,那么这个扫描首先是reader读取了要扫描包的信息,然后reader去扫描
之后把扫描结果,类名,全包名封装起来。下面是它的代码,可以看到。

package com.huterox.spring.framework.beans.config;public class HUBeanDefinition {private String factoryBeanName;private String beanClassName;public void setFactoryBeanName(String factoryBeanName) {this.factoryBeanName = factoryBeanName;}public void setBeanClassName(String beanClassName) {this.beanClassName = beanClassName;}public String getFactoryBeanName() {return factoryBeanName;}public String getBeanClassName() {return beanClassName;}
}

BeanWrapper

这玩意呢,其实是实例化后的对象,同样我们也是把这玩意封装起来了。
在这里插入图片描述
然后我们可以再看到这个代码

package com.huterox.spring.framework.beans;public class HUBeanWrapper {private Object wrapperInstance;private Class<?> wrapperClass;public HUBeanWrapper(Object instance) {this.wrapperInstance = instance;this.wrapperClass = this.wrapperInstance.getClass();}public Object getWrapperInstance() {return wrapperInstance;}public Class<?> getWrapperClass() {return wrapperClass;}
}

getBean()方法

最后就是我们的这个方法,这个方法呢,是我们的核心
在这里插入图片描述

在这里插入图片描述

IOC完整实现

启动/调用

接下来咱们好好聊聊这个IOC

在这里插入图片描述
在要的地方进行启动就可以,这个有点操作系统的意思在里面的,你写的应用程序只是其中一半,另一半是我底层些的系统库,并且这些系统库大部分都是用汇编进行编写的。(还是做上层简单)

完整实现

ok,我们现在来看到完整的代码:

```java
package com.huterox.spring.framework.context;import com.huterox.spring.framework.annotation.HUAutowired;
import com.huterox.spring.framework.annotation.HUController;
import com.huterox.spring.framework.annotation.HUService;
import com.huterox.spring.framework.beans.HUBeanWrapper;
import com.huterox.spring.framework.beans.suports.HUBeanDefinitionReader;
import com.huterox.spring.framework.beans.config.HUBeanDefinition;import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.List;
import java.util.Map;public class HUApplicationContext {private String[] configLocations;private HUBeanDefinitionReader reader;private Map<String, HUBeanDefinition> beanDefinitionMap = new HashMap<>();private Map<String, HUBeanWrapper> factoryBeanInstanceCache = new HashMap<>();private Map<String, Object> factoryBeanObjectCache = new HashMap<>();public HUApplicationContext(String... configLocations) {this.configLocations = configLocations;//加载配置文件读取器this.reader = new HUBeanDefinitionReader(this.configLocations);List<HUBeanDefinition> beanDefinitions = this.reader.doLoadBeanDefinitions();//2.缓存beanDefinitions对象try {doRegistryBeanDefintition(beanDefinitions);} catch (Exception e) {e.printStackTrace();}//3.创建IOC容器,把类都放在IOC容器里面doCreateBean();}private void doCreateBean() {for (Map.Entry<String, HUBeanDefinition> beanDefinitionEntry : this.beanDefinitionMap.entrySet()) {String beanName = beanDefinitionEntry.getKey();//非延时加载getBean(beanName);}}private void doRegistryBeanDefintition(List<HUBeanDefinition> beanDefinitions) throws Exception {//把Definition对象放在咱们的map里面,beanDefinition是保存的类名,全包名for (HUBeanDefinition beanDefinition : beanDefinitions) {//双键存储便于双向查找if (this.beanDefinitionMap.containsKey(beanDefinition.getFactoryBeanName())) {throw new Exception("The " + beanDefinition.getFactoryBeanName() + " is exists!");}this.beanDefinitionMap.put(beanDefinition.getFactoryBeanName(), beanDefinition);this.beanDefinitionMap.put(beanDefinition.getBeanClassName(), beanDefinition);}}//创建Bean的实例,完成依赖注入public Object getBean(String beanName) {//得到对象的全包名,当然这个信息封装在beanDefinition里面HUBeanDefinition beanDefinition = this.beanDefinitionMap.get(beanName);//实例化Object instance = instanceBean(beanName, beanDefinition);if (instance == null) return null;//实例封装为beanWrapper当中HUBeanWrapper beanWrapper = new HUBeanWrapper(instance);//将这个wrapper对象放在容器里面IOC里面this.factoryBeanInstanceCache.put(beanName, beanWrapper);//完成依赖注入populateBean(beanName, beanDefinition, beanWrapper);return this.factoryBeanInstanceCache.get(beanName).getWrapperInstance();}private void populateBean(String beanName, HUBeanDefinition beanDefinition, HUBeanWrapper beanWrapper) {//开始做依赖注入给值Object instance = beanWrapper.getWrapperInstance();Class<?> clazz = beanWrapper.getWrapperClass();Field[] fields = clazz.getDeclaredFields();for (Field field : fields) {if (!(field.isAnnotationPresent(HUAutowired.class))) continue;HUAutowired autowired = field.getAnnotation(HUAutowired.class);String autowiredBeanName = autowired.value().trim();if ("".equals(autowiredBeanName)) {autowiredBeanName = field.getType().getName();}field.setAccessible(true);try {if (!this.factoryBeanInstanceCache.containsKey(autowiredBeanName)) continue;//这个就是为什么要按照标准来写首字母要小写field.set(instance, this.factoryBeanInstanceCache.get(autowiredBeanName).getWrapperInstance());} catch (IllegalAccessException e) {e.printStackTrace();}}}private Object instanceBean(String beanName, HUBeanDefinition beanDefinition) {String className = beanDefinition.getBeanClassName();Object instance = null;try {Class<?> clazz = Class.forName(className);if (!(clazz.isAnnotationPresent(HUService.class) || clazz.isAnnotationPresent(HUController.class))) {return null;}instance = clazz.newInstance();//接下来的这部分是AOP的入口,先忽略//三级缓存,为了解决这种循环依赖的问题,所以在后面进行DI的时候,这里//先进行一个缓存。因为完整的生命周期是对象要完成DI注入后,如果没有先进行缓存//那么在同一时刻进行DI注入的时候,出现A依赖B,B依赖A,并且缓存池里面没有B,要创建B//然后B又要A,A因为没有B又创建不了的情况,先初步缓存这样A要B的时候,创建B然后要依赖A//此时A在创建的时候有个缓存,这样B可以被创建,然后A最后完成注入,同样B也会完成。这部分逻辑后面再说this.factoryBeanObjectCache.put(beanName, instance);} catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {e.printStackTrace();}return instance;}public Object getBean(Class className) {return getBean(className.getName());}public int getBeanDefinitionCount() {return this.beanDefinitionMap.size();}public String[] getBeanDefinitionNames() {return this.beanDefinitionMap.keySet().toArray(new String[this.beanDefinitionMap.size()]);}
}

可以发现,其实这里没啥太多东西,我们目前按照咱的CURD编码习惯,去掉SpringBoot,去掉Mybatis依赖,其实也可以编写出来Lite的Lite版本。

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【C++顺序容器】forward_list的成员函数和非成员函数

目录 forward_list 1. forward_list的成员函数 1.1 构造、析构和赋值运算符重载 1.1.1 构造函数 1.1.2 析构函数 1.1.3 赋值运算符重载 1.2 迭代器 1.3 容量 1.4 元素访问 1.4.1 遍历方法 1.5 修改器 1.6 操作 1.7 观察者 2. forward_list的非成员函数 forward_l…

3.SpringBoot 返回Html界面

1.添加依赖spring-boot-starter-web <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency>2.创建Html界面 在Resources/static 文件夹下面建立对应的html&#xff0c…

实验:如何在YOLO8中添加PnP算法来实现Head Pose Estimation

目录 前言一、步骤二、PnP估计Head Pose&#xff0c;并显示1.引入库2.结果展示 总结 前言 YOLO&#xff18;的集成度比较高&#xff0c;如何在简洁的代码中加入Head Pose的东西&#xff0c;不是一件简单的事情&#xff0e;这里介绍如何插入PnP算法实现头部姿态估计的代码&…

Linux学习之vim在光标所在处写入内容,保存退出

vim insertTest使用vim打开一个叫做insertTest新文件。 输入命令之后&#xff0c;按下Enter&#xff08;回车键&#xff09;&#xff0c;就可以进入下边的操作页面。 按i在当前光标位置处&#xff0c;就可以插入内容了。 接下来写入内容跟记事本里边是差不多的。 按一下…

python_day8_综合案例

综合案例&#xff0c;全球GDP排行榜 1、知识点补充&#xff1a;sort()方法 sort()方法&#xff1a; 列表.sort(key选择排序依据的函数,reverseTrue|False) my_list [[a, 33], [b, 55], [c, 11]]def choose_sort_key(element):return element[1] # 列表中每个元素传进来&am…

Office如何通过VSTO进行PPT插件开发?

文章目录 0.引言1.工具准备2.PPT外接程序创建和生成3.外接程序生成并使用 0.引言 VSTO&#xff08;Visual Studio Tools for Office &#xff09;是VBA的替代&#xff0c;是一套用于创建自定义Office应用程序的Visual Studio工具包。VSTO可以用Visual Basic 或者Visual C#扩展O…

(转载)从0开始学matlab(第11天)—关系运算符和逻辑运算符

选择结构的运算由一个表达式控制的&#xff0c;这个表达式的结果只有 true(1)和 false(0)。有两种形式的运算符可以在 MATLAB 中关系得到 true/false&#xff1a;关系运算符和逻辑运算符。跟 C 语言一样&#xff0c;MATLAB 没有布尔型和逻辑数据类型。MATLAB 把 0 值作为结果fa…

android JSBridge的加载时机问题

https://github.com/lzyzsd/JsBridge 也算是比较悠久和使用了。 可供参考的android和IOS&#xff0c;以及前端的使用 https://segmentfault.com/a/1190000018208609 遇到的问题&#xff1a; 比如&#xff1a; 从前端在加载WebView的时候&#xff0c;执行了某些动作&#xff0c…

FreeRTOS 低功耗模式设计 STM32平台

1. STM32F105RBT6 的三种低功耗模式 1.1 sleep睡眠模式、stop停机模式、standby 待机模式 1.2 STM32中文参考手册有介绍STM32 低功耗模式的介绍 2. FreeRTOS 采用的是时间片轮转的抢占式任务调度机制&#xff0c;其低功耗设计思路一般是&#xff1a; ① 当运行空闲任务&#…

【UE4 C++】08-生成抛射物来模拟攻击效果

步骤 新建一个C类&#xff0c;父类为Actor&#xff0c;命名为“ASMagicProjectile” 在“ASMagicProjectile.h”中添加如下代码&#xff1a; 在“ASMagicProjectile.cpp”中添加如下代码&#xff1a; 编译后在虚幻编辑器中新建一个蓝图&#xff0c;选择父类为我们刚创建的C类…

Java设计模式之行为型-迭代器模式(UML类图+案例分析)

目录 一、基础概念 二、UML类图 三、角色设计 四、案例分析 五、总结 一、基础概念 迭代器模式是一种常用的设计模式&#xff0c;它主要用于遍历集合对象&#xff0c;提供一种方法顺序访问一个聚合对象中的各个元素&#xff0c;而又不暴露该对象的内部表示。 举个简单的…

分布式微服务架构下网络通信的底层实现原理

在分布式架构中&#xff0c;网络通信是底层基础&#xff0c;没有网络&#xff0c;也就没有所谓的分布式架构。只有通过网络才能使得一大片机器互相协作&#xff0c;共同完成一件事情。 同样&#xff0c;在大规模的系统架构中&#xff0c;应用吞吐量上不去、网络存在通信延迟、…