IOC容器创建bean实例的4种方式

news2024/11/25 18:36:48

🎈个人公众号:🎈 :✨✨✨ 可为编程✨ 🍟🍟
🔑个人信条:🔑 知足知不足 有为有不为 为与不为皆为可为🌵
🍉本篇简介:🍉 本篇记录IOC容器创建bean实例的4种方式,如有出入还望指正。

关注公众号【可为编程】回复【面试】领取年度最新面试题!!!

了解了IOC是什么,接下来我们看IOC容器能做什么,首先其最最主要的功能就是对Bean进行管理和创建,IOC容器创建bean实例共有4种方式,具体如下:

  1. 通过反射调用构造方法创建bean对象

  2. 通过静态工厂方法创建bean对象

  3. 通过实例工厂方法创建bean对象

  4. 通过FactoryBean创建bean对象

Spring容器内部创建bean实例对象常见的有4种方式,这四种又可以分为两大种,一是基于反射机制,二是基于工厂模式,我将基于此并结合案例深入说明一下两者的区别和原理。

1、通过反射调用构造方法创建bean对象

调用类的构造方法获取对应的bean实例,是使用最多的方式,这种方式只需要在xml bean元素中指定class属性,spring容器内部会自动调用该类型的构造方法来创建bean对象,将其放在容器中以供使用。

如果是采用注解形式创建和管理Bean,同样也是采用反射的机制,随着Spring的发展,注解(Annotation)逐渐成为主流的配置方式。使用注解可以减少配置文件的代码量,并且把相关的配置信息和代码放在一起,提高了可维护性。例如,使用@Component、@Service、@Repository、@Controller等注解可以自动创建Bean。

关注公众号【可为编程】回复【面试】领取年度最新面试题大全!!!

<bean id="bean名称" name="bean名称或者别名" class="bean的完整类型名称">
<constructor-arg index="0" value="bean的值" ref="引用的bean名称" />
<constructor-arg index="1" value="bean的值" ref="引用的bean名称" />
....
<constructor-arg index="n" value="bean的值" ref="引用的bean名称" />
</bean>

constructor-arg用于指定构造方法参数的值

index:构造方法中参数的位置,从0开始,依次递增

value:指定参数的值

ref:当插入的值为容器内其他bean的时候,这个值为容器中对应bean的名称

举个例子:这里我采用两种方式,首先采用Xml配置文件形配置并式定义Bean,二是采用注解形式生成Bean。

2、 IOC容器初始化细节

Person类
public class Person {

    public String name;
    public Integer age;
    Wife wife;

    public Person(String s, Integer s2, Wife wife) {
        System.out.println("反射通过调用构造函数进行实例创建...");
        this.name = s;
        this.age = s2;
        this.wife = wife;
    }
...省略属性的get() set()方法
}

beans.xml配置

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        ">

    <!--构造方法中增加妻子对象-->
    <bean class="org.kewei.pojo.Person" id="person">
        <constructor-arg type="org.kewei.pojo.Wife" ref="wife"/>
        <constructor-arg index="0" value="可为编程" />
        <constructor-arg index="1" value="18" />
    </bean>
    <bean class="org.kewei.pojo.Wife" id="wife" autowire-candidate="true">
        <property name="age" value="18"/>
        <property name="name" value="可为"/>
    </bean>

</beans>

spring容器创建Person的时候,会通过反射的方式去调用Person类中对应的构造函数来创建Person对象。

public class Main {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("test.xml");
        Person person = (Person) classPathXmlApplicationContext.getBean("person");
        System.out.println(person.name+person.age);
        System.out.println(person.getWifeName() + person.getWifeAge());
    }
}

采用注解@Service定义Bean,如果没有指定BeanId,系统会自动以类名的首字母小写作为Bean名称进行生成。 

@Service
public class KeWeiService {
    public KeWeiService() {

        System.out.println("基于注解形式创建正在创建KeWeiService---   " + this);
        System.out.println("反射通过调用构造函数进行实例创建...---   " + this);
    }
}
//获取Bean
public class Main {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(KeWeiService.class);
        KeWeiService kw = (KeWeiService) annotationConfigApplicationContext.getBean("keWeiService");
        System.out.println(kw);
    }
}

测试结果如下所示,可见都是采用了反射的机制进行Bean的生成创建。只不过两种不同的方式,根本原理上来说还是基于Java的反射原理。

图片

Spring框架在创建Bean时,使用了Java反射(Reflection)机制。这种机制允许Spring在运行时检查和修改对象的一些行为。比如AOP就是采用了反射机制,对Bean可以在运行时对其进行行为修改,比如切面(Aspect),可以拦截目标对象的方法调用,并在调用前后加入额外的逻辑处理,Spring使用反射获取目标方法的信息,并动态地织入额外的代码逻辑。诚然,这块也用到了动态代理技术。

关注公众号【可为编程】回复【面试】领取年度最新面试题大全!!!

总之,Spring容器使用反射来实例化、配置和管理Bean。当Spring容器启动时,它会读取配置文件(例如XML或Java类注释),并根据这些配置信息创建Bean实例。在这个过程中,Spring使用反射来调用对象的构造函数或静态方法来创建Bean,对于属性,Spring还使用反射来设置Bean的属性,当配置文件中定义了一个Bean的属性时,Spring会使用反射调用对象的setter方法来设置这些Bean的属性值。其实反射在Spring的很多地方都有体现,利用Java反射机制Spring实现了延迟加载、依赖注入以及AOP等核心功能。

2、 通过静态工厂方法创建Bean对象

我们还可以采用工厂模式,创建静态工厂,内部提供一些静态方法来生成所需要的对象,将这些静态方法创建的对象交给spring以供后续使用。

<bean id="bean名称" name="" class="静态工厂完整类名" factory-method="静态工厂的方法">
    <constructor-arg index="0" value="bean的值" ref="引用的bean名称" />
    <constructor-arg index="1" value="bean的值" ref="引用的bean名称" />
    ....
    <constructor-arg index="n" value="bean的值" ref="引用的bean名称" />
</bean>

class:指定静态工厂完整的类名

factory-method:静态工厂中的静态方法,返回需要的对象。

constructor-arg用于指定静态方法参数的值,用法和上面介绍的构造方法一样。

spring容器会自动调用静态工厂的静态方法获取指定的对象,将其放在容器中以供使用。

定义静态工厂

创建一个静态工厂类,用于生成Person对象。

public class PersonStaticFactory {

    /**
     * 静态无参方法创建Person
     *
     * @return
     */
    public static Person build() {
        System.out.println(PersonStaticFactory.class + ".buildPerson1()");
        Person person = new Person();
        person.setName("我是无参静态构造方法创建的!");
        return person;
    }

    /**
     * 静态有参方法创建Person
     *
     * @param name 名称
     * @param age  年龄
     * @return
     */
    public static Person build2(String name, int age) {
        System.out.println(PersonStaticFactory.class + ".buildPerson2()");
        Person person2 = new Person();
        person2.setName(name);
        person2.setAge(age);
        return person2;
    }
}
beans.xml配置
<!-- 通过工厂静态无参方法创建bean对象 -->
<bean id="createBeanByStaticFactoryMethod1" class="org.kewei.service.PersonStaticFactory"
factory-method="build"/>
<!-- 通过工厂静态有参方法创建bean对象 -->
<bean id="createBeanByStaticFactoryMethod2" class="org.kewei.service.PersonStaticFactory"
factory-method="build2">
   <constructor-arg index="0" value="通过工厂静态有参方法创建UerModel实例对象"/>
   <constructor-arg index="1" value="30"/>
</bean>

上面配置中,spring容器启动的时候会自动调用PersonStaticFactory中的build()静态方法获取Person对象,将其作为createBeanByStaticFactoryMethod1名称对应的bean对象放在IOC容器当中。

调用PersonStaticFactory的build2()方法,并且会传入2个指定的参数,得到返回的Person对象,将其作为createBeanByStaticFactoryMethod2名称对应的bean对象放在IOC容器中。

ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("test.xml");
Person createBeanByStaticFactoryMethod1 = (Person) classPathXmlApplicationContext.getBean("createBeanByStaticFactoryMethod1");
Person createBeanByStaticFactoryMethod2 = (Person) classPathXmlApplicationContext.getBean("createBeanByStaticFactoryMethod2");
System.out.println(createBeanByStaticFactoryMethod1.name+createBeanByStaticFactoryMethod1.age);
System.out.println(createBeanByStaticFactoryMethod2.name+createBeanByStaticFactoryMethod2.age);
---------------------------------------------
我是无参静态构造方法创建的!null
通过工厂静态有参方法创建Person实例对象30

从输出中可以看出,两个静态方法都被调用了,都输出了对应的信息,第一行为build()方法生成的Bean,第二行为build2()方法生成的Bean对象。

上面是通过配置文件的形式获取Bean对象,接下来我再演示一下通过注解的方式如何通过静态工厂生成Bean对象。

关注公众号【可为编程】回复【面试】领取年度最新面试题大全!!!

注解配置

当然,方法是静态的我们就可以直接调用,但为了演示注解的作用,交给Spring做管理,因此我们通过加注解的形式,获取Bean对象。

@Configuration
public class PersonStaticFactory {

    /**
     * 静态无参方法创建Person
     *
     * @return
     */
    @Bean("createBeanByStaticFactoryMethod1")
    public static Person build() {
        System.out.println(PersonStaticFactory.class + ".buildPerson1()");
        Person person = new Person();
        person.setName("我是无参静态构造方法创建的!");
        return person;
    }

    /**
     * 静态有参方法创建Person
     *
     * @return
     */
    @Bean("createBeanByStaticFactoryMethod2")
    public static Person build2() {
        System.out.println(PersonStaticFactory.class + ".buildPerson2()");
        Person person2 = new Person();
        person2.setName("通过工厂静态有参方法创建");
        person2.setAge(18);
        return person2;
    }
}

通过@Bean注解使用一个静态方法创建一个Bean,并通过Bean的名称(在这里是createBeanByStaticFactoryMethod1)来获取它。首先要保障配置类被Spring扫描到使用@Configuration注解来标记配置类。在非Spring管理的类中直接通过名称获取Bean,需要手动从Spring上下文AnnotationConfigApplicationContext中获取它。

AnnotationConfigApplicationContext annotationConfigApplicationContext1 = new AnnotationConfigApplicationContext(PersonStaticFactory.class);
Person createBeanByStaticFactoryMethod1 = (Person) annotationConfigApplicationContext1.getBean("createBeanByStaticFactoryMethod1");
Person createBeanByStaticFactoryMethod2 = (Person) annotationConfigApplicationContext1.getBean("createBeanByStaticFactoryMethod2");
System.out.println(createBeanByStaticFactoryMethod1.name+createBeanByStaticFactoryMethod1.age);
System.out.println(createBeanByStaticFactoryMethod2.name+createBeanByStaticFactoryMethod2.age);

很显然,比我们用Xml配置的形式,少了好多代码,这也是Spring后期推行的主流方式。

3、通过实例工厂方法创建bean对象

让spring容器去调用某些对象的某些实例方法来生成bean对象放在容器中以供使用。

<bean id="bean名称" factory-bean="需要调用的实例对象bean名称" factory-method="bean对象中的方法">
    <constructor-arg index="0" value="bean的值" ref="引用的bean名称" />
    <constructor-arg index="1" value="bean的值" ref="引用的bean名称" />
    ....
    <constructor-arg index="n" value="bean的值" ref="引用的bean名称" />
</bean>

spring容器以factory-bean的值为bean名称查找对应的bean对象,然后调用该对象中factory-method属性值指定的方法,将这个方法返回的对象作为当前bean对象放在容器中供使用。

定义一个实例工厂

内部写2个方法用来创建Person对象。

public class PersonFactory {

    /**
     * 静态无参方法创建Person
     *
     * @return
     */
    public Person build() {
        System.out.println(PersonFactory.class + ".buildPerson1()");
        Person person = new Person();
        person.setName("我是无参静态构造方法创建的!");
        return person;
    }

    /**
     * 静态有参方法创建Person
     * 关注公众号【可为编程】回复【面试】领取年度最新面试题大全!!!
     *
     * @return
     */
    public Person build2(String name, int age) {
        System.out.println(PersonFactory.class + ".buildPerson2()");
        Person person2 = new Person();
        person2.setName(name);
        person2.setAge(age);
        return person2;
    }
}
beans.xml配置
  <!-- 定义一个工厂实例 -->
  <bean id="personFactory" class="org.kewei.service.PersonFactory"/>
  <!-- 通过userFactory实例的无参user方法创建UserModel对象 -->
  <bean id="createBeanByBeanMethod1" factory-bean="personFactory" factory-method="build"/>
  <!-- 通过userFactory实例的有参user方法创建UserModel对象 -->
  <bean id="createBeanByBeanMethod2" factory-bean="personFactory" factory-method="build2">
      <constructor-arg index="0" value="通过bean实例有参方法创建UserModel实例对象"/>
      <constructor-arg index="1" value="30"/>
  </bean>

createBeanByBeanMethod1对应的bean是通过personFactory的build方法生成的。

createBeanByBeanMethod2对应的bean是通过personFactory的build2方法生成的。

ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("test.xml");
Person createBeanByStaticFactoryMethod1 = (Person) classPathXmlApplicationContext.getBean("createBeanByBeanMethod1");
Person createBeanByStaticFactoryMethod2 = (Person) classPathXmlApplicationContext.getBean("createBeanByBeanMethod2");
System.out.println(createBeanByStaticFactoryMethod1.name+createBeanByStaticFactoryMethod1.age);
System.out.println(createBeanByStaticFactoryMethod2.name+createBeanByStaticFactoryMethod2.age);
---------------------------------------------
我是无参静态构造方法创建的!null
通过bean实例有参方法创建UserModel实例对象30

同样我们可以改成注解的形式

注解配置
@Service
public class PersonFactory {

    /**
     * 静态无参方法创建Person
     *
     * @return
     */
    public Person build() {
        System.out.println(PersonFactory.class + ".buildPerson1()");
        Person person = new Person();
        person.setName("我是无参静态构造方法创建的!");
        return person;
    }

    /**
     * 静态有参方法创建Person
     *
     * @return
     */
    public Person build2(String name, int age) {
        System.out.println(PersonFactory.class + ".buildPerson2()");
        Person person2 = new Person();
        person2.setName(name);
        person2.setAge(age);
        return person2;
    }
}

我们只需要在上面加一个@Service注解,就可以直接调用里面的方法。

 AnnotationConfigApplicationContext annotationConfigApplicationContext1 = new AnnotationConfigApplicationContext(PersonFactory.class);
 PersonFactory personFactory = (PersonFactory) annotationConfigApplicationContext1.getBean("personFactory");
 System.out.println(personFactory.build().name);
---------------------------------------------
class org.kewei.service.PersonFactory.buildPerson1()
我是无参静态构造方法创建的!

4、通过FactoryBean来创建bean对象

前面我们学过了BeanFactory接口,BeanFactory是Spring容器的顶层接口,而这里要说的是FactoryBean,也是一个接口,这两个接口很容易搞混淆,FactoryBean可以让Spring容器通过这个接口的实现来创建我们需要的bean对象。

关注公众号【可为编程】回复【面试】领取年度最新面试题大全!!!

FactoryBean接口源码:

public interface FactoryBean<T> {
    /**
     * 返回创建好的对象
     */
    @Nullable
    T getObject() throws Exception;
    /**
     * 返回需要创建的对象的类型
     */
    @Nullable
    Class<?> getObjectType();
    /**
    * bean是否是单例的
    **/
    default boolean isSingleton() {
        return true;
    }
}

接口中有3个方法,前面2个方法需要我们去实现,getObject方法内部由开发者自己去实现对象的创建,然后将创建好的对象返回给Spring容器;getObjectType需要指定我们创建的bean的类型;最后一个方法isSingleton表示通过这个接口创建的对象是否是单例的,如果返回false,那么每次从容器中获取对象的时候都会调用这个接口的getObject() 去生成bean对象。

<bean id="bean名称" class="FactoryBean接口实现类" />
创建一个FactoryBean实现类​​​​​​​
public class PersonFactoryBean implements FactoryBean<Person> {
    int count = 1;

    @Nullable
    @Override
    public Person getObject() { //1
        Person person = new Person();
        person.setName("我是通过FactoryBean创建的第" + count++ + "对象");//4
        return person;
    }

    @Nullable
    @Override
    public Class<?> getObjectType() {
        return Person.class; //2
    }

    @Override
    public boolean isSingleton() {
        return true; //3
    }
}

//1:返回了一个创建好的Person对象。

//2:返回对象的Class对象。

//3:返回true,表示创建的对象是单例的,那么我们每次从容器中获取这个对象的时候都是同一个对象。

//4:此处用到了一个count,通过这个一会可以看出isSingleton不同返回值的时候从容器获取的bean是否是同一个。

bean xml配置
<!-- 通过FactoryBean 创建Person对象 -->

<bean id="createByFactoryBean" class="org.kewei.service.PersonFactoryBean"/>

启动类增加如下代码:

System.out.println("-------------以下是FactoryBean创建的Bean对象-------------");
//1.bean配置文件位置
String beanXml = "classpath:/test.xml";
//2.创建ClassPathXmlApplicationContext容器,给容器指定需要加载的bean配置文件
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(beanXml);
System.out.println("spring容器中所有bean如下:");
//getBeanDefinitionNames用于获取容器中所有bean的名称
//关注公众号【可为编程】回复【面试】领取年度最新面试题大全!!!
for (String beanName : context.getBeanDefinitionNames()) {
     System.out.println(beanName + ":" + context.getBean(beanName));
}
//多次获取createByFactoryBean看看是否是同一个对象
System.out.println("createByFactoryBean:" + context.getBean("createByFactoryBean"));
System.out.println("createByFactoryBean:" + context.getBean("createByFactoryBean"));

图片

注意最后3行输出,输出的都是同一个createByFactoryBean,并且对象唯一,程序中通过getBean从IOC容器中查找createByFactoryBean3次,3次结果都是相同对象,说明返回的都是同一个Person对象。

下面我们将UserFactoryBean中的isSingleton调整一下,返回false

@Override

public boolean isSingleton() {

return false;

}

当这个方法返回false的时候,表示由这个FactoryBean创建的对象是多例的,那么我们每次从容器中getBean的时候都会去重新调用FactoryBean中的getObject方法获取一个新的对象,再运行一下Client,最后3行输出:

图片

很明显这3次获取的对象不一样,这也是SpringBean的作用域不同,下一篇进行讲解SpringBean的作用域。

总结

SpringIOC容器提供了4种创建bean实例的方式,除了构造函数的方式,其他几种方式可以让我们手动去控制对象的创建,这几种方式大家都掌握一下,能够灵活使用。需要源码的联系我获取。

图片

由表及里分析Spring-IOC容器始末

Spring中的核心概念

不要称之为卷土重来:为什么 Java 仍然会是冠军!

关于高并发你必须知道的几个概念

线程的创建方式对比与线程池相关原理剖析

BigDecimal对象的日常使用汇总

图片

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

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

相关文章

mysql联合索引和最左匹配问题。

1引言&#xff1a; 如果频繁地使⽤相同的⼏个字段查询&#xff0c;就可以考虑建⽴这⼏个字段的联合索引来提⾼查询效率。⽐如对 于联合索引 test_col1_col2_col3&#xff0c;实际建⽴了 (col1)、(col1, col2)、(col, col2, col3) 三个索引。联合 索引的主要优势是减少结果集数量…

深入理解Python中的布尔值:真与假

Python作为一门强大的编程语言&#xff0c;具有丰富的数据类型和逻辑运算&#xff0c;其中布尔&#xff08;Boolean&#xff09;值在控制程序流程和逻辑决策中扮演着关键的角色。本文将深入探讨Python中的布尔值&#xff0c;解释什么是真&#xff08;True&#xff09;和假&…

pytorch加载的cifar10数据集,到底有没有经过归一化

pytorch加载cifar10的归一化 pytorch怎么加载cifar10数据集torchvision.datasets.CIFAR10transforms.Normalize()进行归一化到底在哪里起作用&#xff1f;【CIFAR10源码分析】 torchvision.datasets加载的数据集搭配Dataloader使用model.train()和model.eval() pytorch怎么加载…

Paste v4.1.2(Mac剪切板)

Paste for Mac是一款运行在Mac OS平台上的剪切板小工具&#xff0c;拥有华丽的界面效果&#xff0c;剪切板每一条记录可显示&#xff08;预览&#xff09;文本&#xff0c;图片等记录的完整内容&#xff0c;可以记录最近指定条数的剪切板信息&#xff0c;方便用户随时调用&…

输电线路AR可视化巡检降低作业风险

随着现代工业的快速发展&#xff0c;各行业的一线技术工人要处理的问题越来越复杂&#xff0c;一些工作中棘手的问题迫切需要远端专家的协同处理。但远端专家赶来现场往往面临着专家差旅成本高、设备停机损失大、专业支持滞后、突发故障无法立即解决等痛点。传统的远程协助似乎…

Emacs之高亮显示超过80个字符部分(一百三十)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 人生格言&#xff1a; 人生…

Linux入门知识

​ 文章目录 一、Linux发展历程1.1、Linux前身-Unix1.2、Linux 诞生 二、Linux系统特点三、Linux 分支四、Linux系统架构4.1、系统调用4.2、Linux shell4.3、Linux文件系统4.4、Linux内核 团队博客: 汽车电子社区 一、Linux发展历程 1.1、Linux前身-Unix 1968年Multics 项目…

【Excel】如何画不同时序交叉的百分比堆积柱状图

这里写自定义目录标题 1 将两表交叉合并为一个表1.1 步骤一&#xff1a;在两独立表的工作天数和工资列下面按1-n顺次标号。1.2 步骤二&#xff1a;选中两表需要合并的部分&#xff0c;调出自定义排序1.3 步骤三&#xff1a;选项 ——> 按行排序 &#xff08;选完后点确定&am…

LV.12 D17 中断控制器 学习笔记

一、中断控制器 在处理IRQ的时候&#xff0c;会将CPSR写入IRQ_SPSR&#xff0c;然后将CPU切换为IRQ模式&#xff0c;把状态改成ARM状态&#xff0c;把I位写成1禁止全部的IRQ&#xff0c;所以中断这样是我们不想要的。4412是一个四核的CPU&#xff0c;在发送中断前要确定发送给哪…

【UDS基础】简单介绍“统一诊断服务“

1. 前言 我们将在这个实用教程中介绍UDS的基础知识,重点关注在CAN总线上的UDS(UDSonCAN)和CAN诊断(DoCAN)。此外,我们还会介绍ISO-TP协议,并解释UDS、OBD2、WWH-OBD和OBDonUDS之间的差异。 最后,我们将解释如何请求、记录和解码UDS消息,并提供一些实际示例,例如记录…

libevent

libevent 库概念和特点 开源。精简。跨平台&#xff08;Windows、Linux、maxos、unix&#xff09;。专注于网络通信&#xff08;不一定非用在网络当中&#xff0c;比如下面的读写管道&#xff09;。 libevent特性&#xff1a;基于"事件"&#xff0c;面向“文件描述符…

C++算法:第N位数的原理、源码及测试用例

本文涉及知识点 简单的数学知识。 本博文对应源码&#xff0c;审核比较慢&#xff0c;请耐心等待&#xff1a;https://download.csdn.net/download/he_zhidan/88504919 本博文在CSDN 学院有对应课程。 题目 给你一个整数 n &#xff0c;请你在无限的整数序列 [1, 2, 3, 4, 5…

编译原理(1)----LL(1)文法(首符号集,后跟符号集,选择符号集)

一.首符号集&#xff08;First()&#xff09; 技巧&#xff1a;找最左边可能出现的终结符 例&#xff1a; 1.First(E) E->T,最左边为T&#xff0c;又因为T->F,最左边为F&#xff0c;F->(E)|i,则最左边为{&#xff08;&#xff0c;i } 2.First(T):只需要看符号串最左…

Mac VsCode g++编译报错:不支持C++11语法解决

编译运行时报错&#xff1a; [Running] cd “/Users/yiran/Documents/vs_projects/c/” && g 1116.cpp -o 1116 && "/Users/yiran/Documents/vs_projects/c/"1116 1116.cpp:28:22: warning: range-based for loop is a C11 extension [-Wc11-extensi…

pda条码二维码扫描数据采集安卓手持终端扫码热敏标签打印一体机

HT800新一代移动物联终端是深圳联强优创信息科技有限公司自主研发的基于Android11操作系统的高性能、高可靠的工业级手持数据终端&#xff0c;能与其它设备进行无线通讯&#xff0c;提供良好的操作界面&#xff0c;支持条码扫描、RFID读写&#xff08;NFC&#xff09;、GPS定位…

ch579串口编程笔记

“CH579SFR.h”库文件&#xff0c;关于串口中断部分 /* UART interrupt identification values for IIR bits 3:0 */ #define UART_II_SLV_ADDR 0x0E // RO, UART0 slave address match #define UART_II_LINE_STAT 0x06 // R…

云服务器哪家便宜靠谱 | 简单了解亚马逊云科技发展史

云服务器哪家便宜又靠谱呢&#xff1f;为什么说亚马逊云科技在这道题答案的第一行&#xff0c;一篇故事告诉你。 1994年&#xff0c;杰夫贝索斯在西雅图创建了亚马逊&#xff0c;最初只是一个在线书店。 1997年&#xff0c;亚马逊在纳斯达克交易所上市&#xff0c;成为一家公…

基于 Odoo + Python 的网站快速开发指南

基于 Odoo Python 的网站快速开发指南 下载根据本指南开发的主题模块源码 Odoo 网站生成器是一个灵活的工具&#xff0c;可以轻松构建与 Odoo 应用完全集成的网站。使用其提供的主题选项 (options) 和构建块 (blocks) 很容易定制网站。然而&#xff0c;你还可以更进一步深度定…

JavaEE进阶3

传递数组: 当我们请求中,同一个参数有多个时,浏览器就会帮我们封装成一个数组 用逗号进行分割也是可以的(有的浏览器不能直接使用逗号,需要我们去转码) 传递集合: HTTP 状态码(不是后端自定义的) 2XX:成功 3XX:重定向 4XX:客户端错误 5XX:服务器错误 业务状态码:HTTP响应…

第十章 Python 自定义模块及导入方法

系列文章目录 第一章 Python 基础知识 第二章 python 字符串处理 第三章 python 数据类型 第四章 python 运算符与流程控制 第五章 python 文件操作 第六章 python 函数 第七章 python 常用内建函数 第八章 python 类(面向对象编程) 第九章 python 异常处理 第十章 python 自定…