Java入坑之注解和反射

news2024/10/5 14:15:42

一、注解概念0

1.1基本定义

Java注解是附加在代码中的一些元信息,用于一些工具在编译、运行时进行解析和使用,起到说明、配置的功能 1。它们可以用来标记类、方法、变量、参数和包等

简而言之,注解就是对于代码中某些鲜活个体的贴上去的一张标签。简化来讲,注解如同一张标签。

1.2Java注解分类

Java注解可以分为三类:标准注解,元注解和自定义注解。标准注解是Java语言预定义的一些注解,例如@Override、@Deprecated和@SuppressWarnings。元注解是用来定义其他注解的注解,例如@Retention、@Documented、@Target和@Inherited。此外,您还可以创建自定义注解来满足您的特定需求

1.3内置注解

Java定义了一套内置的注解,共有7个,其中3个在java.lang包中,剩下4个在java.lang.annotation包中。这些注解包括:@Override、@Deprecated、@SuppressWarnings、@Retention、@Documented、@Target和@Inherited 

1.4元注解

元注解是负责对其它注解进行说明的注解,自定义注解时可以使用元注解。Java 5 定义了 4 个注解,分别是 @Documented、@Target、@Retention 和 @Inherited。Java 8 又增加了 @Repeatable 和 @Native 两个注解。这些注解都可以在 java.lang.annotation 包中找到

1.5注解的作用

1.6Java注解的应用

Java注解可以应用于许多方面。它们可以用来提供有关代码的元数据,例如方法是否已被重写(@Override),或者方法是否已被弃用(@Deprecated)。它们还可以用来影响代码的运行方式,例如通过使用 @SuppressWarnings 来抑制编译器警告。此外,注解还可以用于框架中,例如Spring和Hibernate,以提供有关如何处理类和方法的信息。

二、注解

2.1内置注解

2.1.1@Override

在Java中,@Override是一个注解,它用于指示方法是覆盖超类中的方法。当您在子类中编写方法时,如果该方法具有与超类中的方法相同的名称和参数,则可以使用@Override注解来确保您实际上是在覆盖超类中的方法,而不是意外地创建一个新方法。

@Override注解不是必需的,但它可以帮助您更好地理解代码。如果您使用@Override注解,那么如果您意外地更改了超类中的方法签名,Java编译器将会发出错误,提醒您必须更新子类中的方法签名以匹配超类中的方法

public class Animal {
    public void makeSound() {
        System.out.println("The animal makes a sound");
    }
}

public class Dog extends Animal {
    @Override
    public void makeSound() {
        System.out.println("The dog barks");
    }
}

在这个例子中,Dog类继承自Animal类,并覆盖了makeSound()方法。@Override注解用于指示makeSound()方法实际上是在覆盖超类中的方法。

如果试图使用 @Override 标记一个实际上并没有覆写父类的方法时,java 编译器会告警。

class Parent {
    public void foo() {
        System.out.println("Parent's foo");
    }
}

class Child extends Parent {
    @Override
    public void bar() {
        System.out.println("Child's bar");
    }
}

2.1.2@Deprecated

在Java中,@Deprecated是一个注解,用于标明被修饰的类或类成员、类方法已经废弃、过时,不建议使用。


使用@Deprecated注解可以帮助开发人员更好地管理代码库,以确保在未来的版本中,不会意外地使用不再受支持的代码。如果您在您的代码中使用了@Deprecated注解,编译器将会在编译时发出警告。

public class MyClass {
    /**
     * This method is deprecated and should not be used anymore.
     * Use the newMethod() method instead.
     */
    @Deprecated
    public void oldMethod() {
        System.out.println("This method is deprecated.");
    }
    
    public void newMethod() {
        System.out.println("This is the new method.");
    }
}

在这个例子中,MyClass类中的oldMethod()方法被标记为已弃用,并提供了一条注释,说明应该使用newMethod()方法代替。如果您在代码中使用了oldMethod()方法,Java编译器将会发出警告。

注意,@Deprecated注解仅仅是一个标记,它不会强制您停止使用已经被标记为已弃用的代码。但是,使用已经被弃用的代码可能会导致不良后果,因此,您应该尽可能地避免使用它们,除非您确实需要使用它们。

2.1.3@SuppressWarnings

在Java中,@SuppressWarnings是一个注解,它用于告诉编译器忽略特定的警告。当您在编写代码时,Java编译器可能会发出一些警告,这些警告有时可能是不重要的或者您已经处理过了,但这些警告可能会影响代码的可读性。使用@SuppressWarnings注解可以帮助您清除这些无关紧要的警告,使得代码更易于理解。

public class MyClass {
    @SuppressWarnings("unchecked")
    public void myMethod() {
        List myList = new ArrayList(); // 产生警告:使用原始类型
        myList.add("Hello");
        System.out.println(myList);
    }
}

在这个例子中,MyClass类中的myMethod()方法使用了@SuppressWarnings注解,并指定了一个警告类型"unchecked"。这是因为在方法中,我们使用了一个未经泛型化的ArrayList对象,这将产生一个警告,提示我们使用了原始类型。

通过使用@SuppressWarnings注解,我们告诉编译器忽略这个警告,因为我们知道我们在使用ArrayList时,确实不需要使用泛型。

1)抑制单类型的警告

@SuppressWarnings("unchecked")  
public void addItems(String item){  
  @SuppressWarnings("rawtypes")  
   List items = new ArrayList();  
   items.add(item);  
}

2)抑制多类型的警告

@SuppressWarnings(value={"unchecked", "rawtypes"})  
public void addItems(String item){  
   List items = new ArrayList();  
   items.add(item);  
}

3)抑制所有类型的警告

@SuppressWarnings("all")  
public void addItems(String item){  
   List items = new ArrayList();  
   items.add(item);  
}

@SuppressWarnings 注解的常见参数值的简单说明:


1."unchecked":指示编译器在使用未经检查的转换时不发出警告。使用泛型时,可能会出现“未经检查的转换”警告,这是因为泛型擦除导致在运行时无法确定类型。但是在某些情况下,我们确信类型是安全的,因此可以使用@SuppressWarnings("unchecked")注解来抑制警告。

2."deprecation":指示编译器在使用已弃用的方法或类时不发出警告。如果您使用了已经被标记为已弃用的方法或类,Java编译器将会发出一个“已弃用”的警告,使用@SuppressWarnings("deprecation")可以抑制该警告。

3."rawtypes":指示编译器在使用未经泛型化的类或接口时不发出警告。使用泛型时,应该避免使用未经泛型化的类或接口,但是如果确实需要使用,可以使用@SuppressWarnings("rawtypes")来抑制警告。

4."unused":指示编译器在使用未使用的变量、方法或字段时不发出警告。未使用的代码可能会降低代码的可读性,但在某些情况下,我们可能需要暂时保留未使用的代码。使用@SuppressWarnings("unused")可以抑制该警告。

5."all":指示编译器在所有警告类型上都不发出警告。使用@SuppressWarnings("all")可以抑制所有警告,但这种做法不推荐,因为它会隐藏真正的问题
 

2.1.4@FunctionalInterface

@FunctionalInterface是Java 8中引入的一个内置注解,用于标记一个接口为函数式接口。函数式接口指的是只有一个抽象方法的接口,它们通常用于支持Java中的Lambda表达式和方法引用。

使用@FunctionalInterface注解可以确保接口仅有一个抽象方法,并提供了一种简单的方法来检查接口是否符合函数式接口的标准。如果标记为@FunctionalInterface的接口具有多个抽象方法,则编译器会抛出一个错误。

函数式接口(Functional Interface)就是一个有且仅有一个抽象方法,但是可以有多个非抽象方法的接口。

@FunctionalInterface
public interface MyInterface {
    void doSomething();
}

在上面的示例中,MyInterface接口只有一个抽象方法doSomething(),因此可以使用@FunctionalInterface注解进行标记。如果您在该接口中添加另一个抽象方法,编译器会抛出错误,因为这个接口不再符合函数式接口的标准。

2.2元注解

元注解是一种注解,它们用于注解其他注解。Java定义了几种元注解,它们可以用于自定义注解,以控制注解的行为和用途

2.2.1@Targe

@Target是Java内置注解之一,用于指定注解可以应用的元素类型。该注解可以应用在自定义注解上,用于控制自定义注解可以应用的元素类型,表示我们注解可以用在哪些地方包括:

@Target(ElementType.TYPE)
public @interface MyAnnotation {
    String value();
}

在上面的示例中,MyAnnotation注解使用@Target注解指定了它只能应用于类、接口或枚举类型。如果您尝试将MyAnnotation注解应用于字段或方法等其他元素类型,则编译器会抛出错误。

使用@Target注解可以帮助控制自定义注解的应用范围,使其更加灵活和通用。

2.2.2@ Retention

@ Retention用来定义该注解在哪一个级别可用在源代码中(SOURCE)、类文件中(CLASS)或者运行时(RUNTIME)。

@Retention是Java内置注解之一,用于指定注解的保留时间。它包含三个值:SOURCE、CLASS和RUNTIME。其中,SOURCE表示注解仅在源代码中可见,不会被编译器编译进字节码文件中;CLASS表示注解会被编译器编译进字节码文件中,但在运行时不会被JVM保留;RUNTIME表示注解会被编译器编译进字节码文件中,并在运行时保留在JVM中,可以通过反射来获取注解信息。

@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    String value();
}

在上面的示例中,MyAnnotation注解使用@Retention注解指定了它在运行时会被保留。这意味着可以在运行时通过反射来获取注解信息,以便执行一些特定的操作。

使用@Retention注解可以帮助控制注解的保留时间,使其可以被用于不同的场景。例如,在编写ORM框架时,可以使用@Retention(RetentionPolicy.RUNTIME)来标记注解,以便在运行时动态生成SQL语句。

2.2.3@Documented

@Documented是Java内置注解之一,用于指定注解是否包含在Java文档中。如果一个注解被标记了@Documented,则在使用javadoc生成Java文档时,该注解的信息将会被包含在文档中。

@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    String value();
}

在上面的示例中,MyAnnotation注解使用@Documented注解指定了它在Java文档中应该被包含。这意味着在使用javadoc生成Java文档时,该注解的信息将会被包含在文档中,以便其他开发人员可以更好地理解如何使用该注解。

2.2.4@Inherited

@Inherited是Java内置注解之一,用于指定一个注解是否可以被继承。如果一个注解被标记了@Inherited,则当子类继承了被标记的注解时,子类也会被自动标记上该注解。

@Inherited
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    String value();
}

在上面的示例中,MyAnnotation注解使用@Inherited注解指定了它可以被继承。这意味着如果一个类继承了被标记了@MyAnnotation注解的父类,则子类也会被自动标记上该注解。

使用@Inherited注解可以帮助提高注解的复用性,使得一些常见的注解可以被自动继承,从而减少代码的重复性。同时,使用@Inherited注解也可以避免因为父类上的注解没有被继承而导致一些意外的问题。

2.2.5@Repeatable

在Java 8及以上版本中,我们可以使用@Repeatable注解来标记一个注解可以重复使用多次。这样,我们就可以在一个元素上同时使用多个相同的注解,而不必像以前那样使用容器类型来包含多个注解。

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Repeatable(MyAnnotations.class)
public @interface MyAnnotation {
    String value();
}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface MyAnnotations {
    MyAnnotation[] value();
}

@MyAnnotation("foo")
@MyAnnotation("bar")
public class MyClass {
    // Class body here
}

在上面的示例中,我们定义了一个名为MyAnnotation的注解,并使用@Repeatable注解标记该注解可以重复使用多次。我们还定义了一个名为MyAnnotations的注解来包含多个MyAnnotation注解。最后,我们在MyClass类上同时使用了两个@MyAnnotation注解。

2.3自定义注解

当我们理解了内置注解, 元注解和获取注解的反射接口后,我们便可以开始自定义注解了。

创建自定义注解和创建一个接口相似,但是注解的interface关键字需要以@符号开头,我们可以为注解声明方法。

自定义注解格式:

// 元注解
public @interface 注解名称{
    // 属性列表
}

2.3.1创建自定义注解

在Java中,我们可以使用@interface关键字来定义自己的注解,这和定义接口或类类似。

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME) // 注解保留到运行时
@Target(ElementType.METHOD) // 注解作用于方法
public @interface MyAnnotation {
    String value() default ""; // 定义一个名为value的属性,类型为String
}

在上面的示例中,我们使用@interface关键字定义了一个名为MyAnnotation的注解,并使用@Retention和@Target元注解指定注解的保留时间和目标范围。我们还定义了一个名为value的属性,类型为String,并使用default关键字指定了属性的默认值。

使用自定义注解时,我们可以为注解的属性赋值

public class MyClass {
    @MyAnnotation("Hello, world!")
    public void myMethod() {
        // Method body here
    }
}

在上面的示例中,我们在myMethod()方法上使用了@MyAnnotation注解,并为value属性赋了一个字符串值。

在运行时,我们可以通过反射来获取注解的信息,以便进行一些特定的操作,例如:

Method method = MyClass.class.getMethod("myMethod");
MyAnnotation annotation = method.getAnnotation(MyAnnotation.class);
String value = annotation.value(); // 获取注解属性的值

以上代码将获取MyClass类中的myMethod()方法上的@MyAnnotation注解,并获取注解的value属性的值。


2.3.2使用自定义注解

一旦我们定义了自己的注解,我们就可以在代码中使用它们来添加额外的元数据信息。下面是一个示例,演示如何使用自定义注解:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyAnnotation {
    String value() default "";
}

public class MyClass {
    @MyAnnotation("Hello, world!")
    public void myMethod() {
        // Method body here
    }
}

public class Main {
    public static void main(String[] args) throws NoSuchMethodException {
        MyClass obj = new MyClass();
        Method method = obj.getClass().getMethod("myMethod");
        MyAnnotation annotation = method.getAnnotation(MyAnnotation.class);
        String value = annotation.value(); // 获取注解属性的值
        System.out.println(value); // 输出 "Hello, world!"
    }
}

在上面的示例中,我们定义了一个名为MyAnnotation的注解,并将其应用于MyClass类中的myMethod()方法上。我们还定义了一个名为Main的类,用于演示如何使用反射获取注解的信息。

在运行时,我们使用反射来获取MyClass对象的myMethod()方法,并获取其上的@MyAnnotation注解。然后,我们从注解中获取其属性值,并将其打印到控制台上。

2.3.3测试自定义注解

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyTest {
    String value() default "";
}

public class Calculator {
    public int add(int a, int b) {
        return a + b;
    }

    @MyTest("testAdd")
    public void testAdd() {
        int result = add(2, 3);
        if (result == 5) {
            System.out.println("Test passed!");
        } else {
            System.out.println("Test failed.");
        }
    }
}

public class TestRunner {
    public static void main(String[] args) throws NoSuchMethodException {
        Calculator calculator = new Calculator();
        Method method = calculator.getClass().getMethod("testAdd");
        MyTest testAnnotation = method.getAnnotation(MyTest.class);
        String testName = testAnnotation.value();
        System.out.println("Running test: " + testName);
        calculator.testAdd();
    }
}

在上面的示例中,我们定义了一个名为MyTest的注解,并将其应用于Calculator类中的testAdd()方法上。该方法执行了一个简单的加法测试,用于验证add()方法的正确性。如果测试通过,则打印“Test passed!”消息;否则,打印“Test failed.”消息。

在TestRunner类中,我们使用反射来获取Calculator对象的testAdd()方法,并获取其上的@MyTest注解。我们从注解中获取其属性值,并将其打印到控制台上。最后,我们调用testAdd()方法以运行测试。

三、反射概述

反射是框架设计的灵魂

(使用的前提条件:必须先得到代表的字节码的Class,Class类用于表示.class文件(字节码))

3.1概述

Java反射是指在Java运行时环境中,对于任意一个类,都能够知道这个类的所有属性和方法,对于任意一个对象,都能够调用它的任意一个方法,这种动态获取信息以及动态调用对象方法的能力就是Java反射机制。

3.2反射机制功能

Java反射机制提供了一种动态获取类信息和操作类成员的机制,能够使得Java程序可以在运行时分析自身的内部结构。利用Java反射机制,可以实现如下功能:

1.在运行时动态获取类的信息,包括类名、父类信息、接口信息、构造方法信息、字段信息、方法信息等。

2.在运行时动态创建对象实例,通过反射获取构造函数并调用构造函数创建对象。

3.在运行时动态访问对象的属性和方法,通过反射获取对象属性和方法并进行操作。

4.在运行时动态调用方法,可以在编译时不确定方法的名称,而是在运行时动态获取方法名称并进行调用。

3.3理解Class类并获取Class实例

 

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

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

相关文章

企业如何开发自己的小程序

小程序是一种轻量级的应用程序,被广泛用于社交娱乐、电商购物等领域。对于企业而言,开发自己的小程序可以为客户提供更加个性化的服务,提高品牌认知度和用户忠诚度。本文将介绍企业如何开发自己的小程序,并通过一个具体的案例来说…

【CSS】图片底部空白缝隙处理 ( 使用居中对齐 / 顶部对齐 / 底部对齐 | 将行内元素 / 行内块元素转为块级元素 )

文章目录一、图片底部空白缝隙问题二、图片底部空白缝隙问题解决方案一 ( 使用居中对齐 / 顶部对齐 / 底部对齐 )三、图片底部空白缝隙问题解决方案二 ( 将行内元素 / 行内块元素转为块级元素 )一、图片底部空白缝隙问题 在上一篇博客中 , 使用默认的基线对齐 , 会发现 行内块级…

java 利用正则来分析日志(IT枫斗者)

利用正则来分析日志(IT枫斗者) 环境接口的历史并发数,然而运维并没有做相关的统计,没办法,只能拿到服务器近一个月的 Nginx access 日志,根据正则匹配所有我的接口服务的日志,然后统计每一秒内…

《低代码PaaS驱动集团企业数字化创新白皮书》-平台化加低代码提供破解之道(2)

平台化加低代码提供破解之道 低代码向业务的赋能:以效率和创新为核心,提升组织效率,促进创新,优化体验 通过IDC对大型企业的调研发现,当前拥有100个及以上应用数量的企业已经高达70%;IDC预测 ,2025年&…

〖Python网络爬虫实战⑮〗- pyquery的使用

订阅:新手可以订阅我的其他专栏。免费阶段订阅量1000python项目实战 Python编程基础教程系列(零基础小白搬砖逆袭) 说明:本专栏持续更新中,目前专栏免费订阅,在转为付费专栏前订阅本专栏的,可以免费订阅付费…

数据结构——队列(C语言实现)

队列的概念与结构 队列是一种特殊的线性结构,数据只能在一端插入,数据也只能在另一端进行删除。插入数据的那一端称之为队尾,插入数据的动作称之为入队。删除数据的那一端称之为队头,删除数据的动作称之为出列。队列遵守的是FIFO…

LeetCode 189.轮转数组

文章目录💡题目分析💡解题思路🚩思路1:暴力求解 --- 旋转k次🔔接口源码:🚩思路2:额外开数组🔔接口源码:🚩思路3:三段逆置📍算法设计🔔接口源码&am…

JavaWeb开发 —— Web入门

目录 一、Spring 二、SpringBootWeb快速入门 三、HTTP协议 1. 概述 2. 请求协议 3. 响应协议 四、Web服务器 - Tomcat 1. 介绍 2. 基本使用 3. 入门程序解析 一、Spring ① 官网:http://spring.io ② Spring 发展到今天已经形成了一种开发生态圈&…

2022 idea 从原型创建maven项目框架--以创建niif-processors为列

目录一、idea配置二、下载archetype-catalog.xml文件三、创建设置四、创建成功截图一、idea配置 在如下两张图片花圈的位置添加如下参数 -Dmaven.wagon.http.ssl.insecuretrue -Dmaven.wagon.http.ssl.allowalltrue -Dmaven.wagon.http.ssl.ignore.validity.datestrue 二、下载…

Qt Quick - 导航控件综述

Qt Quick - 导航控件综述一、概述二、StackView控件三、SwipeView控件四、TabBar控件五、TabButton控件一、概述 Qt Quick Controls提供了一系列导航模型。 控件功能Drawer可以用滑动手势打开和关闭的侧滑动面板StackView提供基于堆栈的导航模型SwipeView允许用户通过横向滑动…

面试官:谈谈你对TypeScript有什么了解

TypeScript 相关面试题1.说说你对 TypeScript 的理解?与 JavaScript 的区别是什么特性区别2.说说你对 TypeScript 中类的理解?应用场景?是什么使用方式继承修饰符私有修饰符受保护修饰符只读修饰符静态属性抽象类应用场景3.说说 typescript 的…

简单易用的公司网页模板,助您快速建站

在当今数字化时代,拥有一个高质量的公司网页是每个企业成功的关键。然而,对于那些没有技术专业知识的人来说,创建一个专业的网页可能是一项艰巨的任务。但是,现在有许多简单易用的公司网页模板可用于帮助您快速建站。 下面&#…

【CSS】文字溢出问题 ( 强制文本在一行中显示 | 隐藏文本的超出部分 | 使用省略号代替文本超出部分 )

文章目录一、文字溢出问题二、文字溢出处理方案三、代码示例一、文字溢出问题 在元素对象内部显示文字 , 如果文本过长 , 则会出现文本溢出的问题 ; 下面的示例中 , 在 150x25 像素的盒子中 , 显示 骐骥一跃,不能十步;驽马十驾,功在不舍; 一段话 , 明显…

【初识C++】(缺省参数和函数重载)

文章目录一、缺省参数1.缺省参数定义2.缺省参数分类2.1全缺省参数2.2半缺省参数二、函数重载1.函数重载概念2.构成重载的几种方式为什么会有函数重载及其原理一、缺省参数 1.缺省参数定义 缺省参数是在函数的声明中给定参数一个指定的值。 如果传参没有给定参数,那…

三百左右蓝牙耳机选哪个?300左右无线蓝牙耳机推荐

多数人消遣的方式一般是听听音乐玩玩游戏,想要更好的体验感最少不了的一定就是蓝牙耳机了,可对于大多数人来说,irpods之类的属实太贵了,所以更多人追求性价比,之前也买过不靠谱的耳机,用几天就坏了&#xf…

MySQL数据库:索引

一、索引简介 1.概念 索引是一种特殊的文件,包含着对数据表里所有记录的引用指针。可以对表中的一列或多列创建索引,并指定索引的类型,各类索引有各自的数据结构实现。 相当于是给数据库中的数据建立了一个目录,通过目录可以知道…

QT多线程的5种用法,通过使用线程解决UI主界面的耗时操作代码,防止界面卡死。

QT多线程5种用法第一种 主线程(GUI)第二种 子线程1继承自QThread头文件 movetothread4.h源文件 movetothread4.cpp子线程1对象的创建第二种 子线程2继承自QThread头文件源文件对象创建位置(销毁)第三种 子线程3继承自QThread头文件源文件对象的创建第四种…

基于matlab仿真机械手

一、前言该示例显示了处于主动立体视觉模式的操纵器。它说明了立体渲染属性的效果以及如何使用立体视觉 VRFIGURE 属性。仅当图形卡支持四缓冲 OpenGL 渲染并由图形卡驱动程序启用时,操纵器才会以活动立体视觉模式显示。请注意,只有当您使用带有主动快门…

Android 布局 Fragment

Android 布局 FragmentFragment出现的初衷生命周期onCreate()onPause()onAttach()onCreateView()onActivityCreated()onDestroyView()onDetach()您可能还想扩展几个子类,而非 Fragment 基类:DialogFragmentListFragmentPreferenceFragmentCompat同系列文…

2.3.2单链表的插入删除

按位序插入(带头结点) 将第i-1个结点的指针指向第i个结点。 头节点看作是第0个结点。 s->datae //设定s指针的数据域为e s->nextp->next //将p指针指向的位置赋值给s指针指向的位置 p->nexts //再将s的数据域赋值给p指针指向的位置…