接口
接口
◆普通类:只有具体实现
◆抽象类:具体实现和规范(抽象方法)都有!
◆接口:只有规范! 自己无法写方法~ 专业的约束 约束和实现分离:面向接口编程
-
接口就是规范,定义的是一组规则,体现了现实世界中“如果你是...则必须能..”的思想。如果你是天使则必须能飞。如果你是汽车,则必须能跑。如果你好人,则必须干掉坏人;如果你是坏人,则必须欺负好人。
-
接口的本质是契约,就像我们人间的法律一样。制定好后大家都遵守。
-
oop的精髓,是对对象的抽象,最能体现这一点的就是接口。为什么我们讨论设计 模式都只针对具备了抽象能力的语言(比如c++、java、c#等),就是因为设计模式所研究的,实际上就是如何合理的去抽象。
接口(Interface)是面向对象编程中的一种结构,它定义了一组方法的签名(方法名、参数列表、返回类型),但没有方法体,意味着它仅仅描述了类应该
做什么,而没有描述如何做。接口的主要作用是提供一种统一的规范,强制实现接口的类必须实现接口中定义的所有方法。
接口用于为不同的类提供一个共同的行为约定,即让多个类实现相同的方法,而不关注这些类如何实现这些方法。
接口中的方法
- 默认修饰符:
public abstract- 接口中的普通方法(非静态或默认方法)是抽象方法,必须由实现类实现。
- 显式声明
public和abstract是多余的,可以直接写方法签名。 - 从 Java 8 开始,接口可以有默认方法和静态方法。
interface Example {
void doSomething(); // 等价于 public abstract void doSomething();
default void defaultMethod() {
System.out.println("这是一个默认方法!");
}
static void staticMethod() {
System.out.println("这是一个静态方法!");
}
}
2. 接口中的变量
- 默认修饰符:
public static final- 接口中的变量是常量,默认是
public static final,必须初始化。 - 变量名通常使用大写字母加下划线表示。
- 接口中的变量是常量,默认是
interface Example {
int CONSTANT = 100; // 等价于 public static final int CONSTANT = 100;
}
3. 接口中的构造方法
-
接口中不能有构造方法
- 因为接口不能被实例化,所以无法定义构造方法。
4. 接口中的嵌套类型
-
嵌套类默认是
public static- 接口内部可以定义嵌套类、接口或枚举,它们默认是
static的。
- 接口内部可以定义嵌套类、接口或枚举,它们默认是
interface Example {
class NestedClass {
void print() {
System.out.println("接口中的嵌套类!");
}
}
}
扩展:默认方法与静态方法(Java 8+)
1. 默认方法 (default)
- 默认方法允许接口提供方法的默认实现。
- 实现类可以选择性地重写默认方法。
interface Example {
default void sayHello() {
System.out.println("这是接口的默认方法!");
}
}
class Implementation implements Example {}
public class Test {
public static void main(String[] args) {
Example e = new Implementation();
e.sayHello(); // 输出:这是接口的默认方法!
}
}
2. 静态方法 (static)
- 静态方法属于接口本身,不能通过实现类调用,必须使用接口名调用。
interface Example {
static void greet() {
System.out.println("这是接口的静态方法!");
}
}
public class Test {
public static void main(String[] args) {
Example.greet(); // 输出:这是接口的静态方法!
}
}
小结:
- 方法默认是
public abstract:必须实现,或者在 Java 8+ 中使用默认方法提供实现。 - 变量默认是
public static final:常量,必须初始化。 - 嵌套类型默认是
public static:适用于接口内部的类、接口和枚举。 - 没有构造方法:接口不能被实例化,但可以通过实现类或匿名类使用。
通过默认方法和静态方法,接口在 Java 8 之后不仅仅是定义行为的规范,也具备了一定的功能性和扩展能力。
接口具体实现
接口的定义 关键字 interface
package com.oop.demo10;
//测试 interface 接口
/*
* 1.不能包含方法实现(Java 8+ 可包含默认方法和静态方法):方法默认为 public abstract。
* 2.不能定义普通成员变量:只能定义常量(public static final)。
* 3.没有构造方法:因为接口不能实例化。
* 4.支持多继承:一个类可以实现多个接口,一个接口也可以继承多个接口。
* 5.访问修饰符:所有成员默认是 public。
* */
public interface UserService {
int num=9; //在接口中,int num = 9; 不会报错,这是因为接口中的所有变量默认被
// 隐式地 修饰为 public static final。换句话说,int num = 9; 实际上等价于: //public static final int num = 9;
default void sayHello() { //java8之后 接口内可以定义默认方法 可以被实现类继承
System.out.println("hello");
}
static void sayHi() { //java8之后 接口可以定义静态方法 使用是 接口名.静态方法
System.out.println("hi");
}
//public void run(){} //同抽象类一样,接口中的方法没有方法体,写了就报错
public abstract void run();//接口中定义的方法约束 默认就是 public abstract
void eat(); //相当于public abstract void eat();
//public UserService(){} //接口中没有构造方法 接口不能实例化
}
接口中方法的实现 实现类 关键字 implements
package com.oop.demo10;
//这个类 对接口UserService 中抽象方法得具体的实现 重写
//实现接口的类 必须实现接口中的方法!
public class UserServiceImpl implements UserService {
/*
* @Override //接口中得默认方法也可以被重写!
public void sayHello() {
UserService.super.sayHello();
}*/
@Override
public void run() {
System.out.println("RUN");
}
@Override
public void eat() {
System.out.println("EAT");
}
}
实现类
package com.oop.demo10;
//Service类 将UserService接口中的方法实现后,做一个是实现类多态重建对象 然后调用方法
public class Application {
public static void main(String[] args) {
UserService s1 = new UserServiceImpl(); //接口定义指向实现类实例
s1.sayHello();//默认方法的调用
UserService.sayHi();//接口静态方法的调用
System.out.println(s1.num);
//s1.num=10; //public static final 常量 不可改变值
System.out.println(s1.num);
s1.run();//方法的重写实现接口内 函数的具体实现
s1.eat();
UserService s2 = new UserServiceImpl(); //接口定义指向实现类实例
System.out.println(s2.num);
}
}
输出结果:
hello
hi
9
9
RUN
EAT
9
1. 接口中的变量
定义:
int num = 9;
- 解释
- 接口中的变量默认是
public static final,即常量。- 这意味着
num是一个类级别的常量,可以通过UserService.num访问。- 由于是
final,所以无法修改num的值,s1.num = 10;会导致编译错误。
2. 接口中的默认方法
定义:
default void sayHello() { System.out.println("hello"); }
- 解
- 默认方法在接口中有方法体,提供了一个默认实现,避免每个实现类都必须实现该方法。
- 默认方法可以直接被实现类继承,也可以选择重写。
- 调用时,可以通过接口的引用调用默认方法,例如:
s1.sayHello();。
3. 接口中的静态方法
定义:
static void sayHi() { System.out.println("hi"); }
- 解释
- 静态方法属于接口本身,不能被实现类继承。
- 调用时必须通过接口名调用,例如:
UserService.sayHi();。- 这保证了静态方法不会被子类重写,从而保持接口定义的一致性。
4. 接口中的抽象方法
定义:
public abstract void run(); void eat(); // 等价于 public abstract void eat();
- 解释
- 接口中的抽象方法是默认
public abstract的,可以省略修饰符。- 实现类必须提供这些抽象方法的具体实现,否则会报错。
5. 接口没有构造方法
定义:
// public UserService() {} // 不能定义构造方法
- 解释
- 接口是用来定义行为规范的,不是用来创建对象的,因此没有构造方法。
- 接口不能直接实例化,只能通过实现类来创建对象。
6. 实现类中的方法实现
在
Service类中,实现了接口中的抽象方法:@Override public void run() { System.out.println("RUN"); } @Override public void eat() { System.out.println("EAT"); }
- 解释
Service类是UserService接口的实现类,必须实现接口中的所有抽象方法。- 默认方法
sayHello()也可以选择重写,如果不重写,调用时会使用接口中的默认实现。
7. 测试类的多态性
在
Application类中,使用接口的多态性测试了接口和实现类的功能:UserService s1 = new Service(); // 接口引用指向实现类对象 s1.sayHello(); // 调用接口的默认方法 UserService.sayHi(); // 调用接口的静态方法 System.out.println(s1.num); // 输出常量值 s1.run(); // 调用实现类的重写方法 s1.eat(); // 调用实现类的重写方法
- 多态性
UserService是接口,s1是它的引用,实际指向的是Service类的对象。- 通过多态性,接口的引用可以调用实现类中的具体实现方法(
run()和eat())。- 默认方法和静态方法可以通过接口直接使用,无需依赖实现类。
总结:接口的特点与应用
特性 解释 变量 接口中的变量默认是 public static final,必须初始化,且值不可修改。抽象方法 默认是 public abstract,实现类必须实现这些方法。默认方法 使用 default定义,有方法体,可以直接被实现类继承或选择性重写。静态方法 使用 static定义,属于接口本身,不能被实现类继承,必须通过接口名调用。多态性 接口可以作为引用类型,指向实现类的实例,调用实现类的方法。 没有构造方法 接口不能被实例化,只能通过实现类创建对象。 多继承支持 一个类可以实现多个接口,从而同时继承多个接口的行为规范(类似多继承)。 通过接口的设计,可以规范实现类的行为,同时增加代码的灵活性和扩展性。Java 8 之后的默认方法和静态方法进一步增强了接口的功能,使其更为强大和实用。
dog d1 = new dog(); //通过是实现类的实例化 调用各个接口的 在是实现类中重写的方法 缺点是调用不到 接口中的默认和静态方法 这句注释理解的对吗
ChatGPT
您的注释中对 实现类实例化和接口方法调用 的描述是 部分正确的,但需要稍微调整和补充,特别是关于接口中的 默认方法 和 静态方法 的调用。以下是逐项解析:
1. 关于实现类实例化
Dog d1 = new Dog();
- 解释
Dog是实现类,通过直接实例化Dog,可以调用Dog类中的所有方法,包括从接口继承的方法。- 通过实现类的实例化,调用的接口方法实际上是
Dog类中 实现或重写的版本。
2. 关于默认方法的调用
- 您的描述:“调用不到接口中的默认方法。”
- 实际情况:不完全正确。
默认方法的继承规则:
默认方法可以被实现类继承,并且可以通过实现类的实例调用。
如果实现类没有重写默认方法,那么默认方法的实现来自接口。
如果实现类重写了默认方法,那么调用的是实现类中的方法。
示例:
interface UserService { default void sayHello() { System.out.println("接口的默认方法:Hello!"); } } class Dog implements UserService { // 继承了接口的默认方法 } public class Test { public static void main(String[] args) { Dog d1 = new Dog(); d1.sayHello(); // 输出:接口的默认方法:Hello! } }调整后的理解:
- 默认方法是可以通过实现类的实例调用的。
- 如果您通过
Dog d1 = new Dog();实例化Dog,默认方法可以被直接调用,除非它在实现类中被重写。
3. 关于静态方法的调用
- 您的描述:“调用不到接口中的静态方法。”
- 实际情况:正确。
静态方法的访问规则:
- 静态方法属于接口本身,不能通过实现类或实现类的实例调用。
- 必须通过接口名来调用静态方法。
示例:
interface UserService { static void sayHi() { System.out.println("接口的静态方法:Hi!"); } } class Dog implements UserService { // 静态方法不能被继承 } public class Test { public static void main(String[] args) { // 不能通过实例调用 // Dog d1 = new Dog(); // d1.sayHi(); // 错误 // 必须通过接口名调用 UserService.sayHi(); // 输出:接口的静态方法:Hi! } }调整后的理解:
- 静态方法不能通过实现类实例调用。
- 只能通过接口名调用,例如:
UserService.sayHi();。总结:接口的默认方法和静态方法的调用规则
方法类型 是否可以通过实现类实例调用? 是否可以通过接口名调用? 说明 默认方法 可以(如果未被重写) 否 默认方法可以被实现类继承,通过实例调用。 静态方法 否 可以 静态方法属于接口本身,不能被实现类或其实例调用。 通过实现类实例化时,您可以调用实现类中的方法(包括继承自接口的方法),但静态方法始终只能通过接口名调用。
接口多实现、接口多继承及区别
接口多实现
package com.oop.demo11;
//本例实现 类对多接口的实现
public class Application {
public static void main(String[] args) {
dog d1 = new dog(); //通过是实现类的实例化 调用各个接口的 在是实现类中重写的方法
// 事实上 这种方案也可以调用接口中的默认方法 静态方法(如果有那就)
/*默认方法可以被实现类继承,并且可以通过实现类的实例调用。
如果实现类没有重写默认方法,那么默认方法的实现来自接口。
如果实现类重写了默认方法,那么调用的是实现类中的方法。*/
d1.eat();
d1.run();
d1.sound();
}
}
interface Animal{
void sound();
void eat();
}
interface Runnale{
void run();
}
class dog implements Animal,Runnable{ //实现类对接口的多实现
@Override
public void sound() {
System.out.println("Bark Bark!");
}
@Override
public void eat() {
System.out.println("Dog is eating!");
}
@Override
public void run() {
System.out.println("Dog is running!");
}
}
接口多继承
package com.oop.demo11;
//本例实现 多接口继承
public class Application02 {
public static void main(String[] args) {
landAnimal d1 = new Dog();
d1.eat();
d1.run();
d1.sound();
}
}
interface Animal{
void eat();
void run();
}
interface Runnable{
void run();
}
interface landAnimal extends Animal, Runnable{ //接口陆地动物实现对Animal 、Runnable两个接口的继承 接口多继承
void sound();//继承接口自己的方法
}
class Dog implements landAnimal{ //实现类对 继承接口的实现 可以重写所有继承接口的方法 然后利用多态(父类接口的引用指向子类实现类的实例)实现方法调用
@Override
public void sound() {
}
@Override
public void eat() {
}
@Override
public void run() {
}
}
区别对比
在您的代码中,以下两种方式有本质的区别,主要体现在 多态性 和 调用方法的范围 方面。
1 使用实现类作为引用类型:
Dog d1 = new Dog();Dog d1 = new Dog();特点:
实现类的引用指向自己的实例
d1的编译时类型和运行时类型都是Dog。可以调用
Dog类中所有定义的方法,包括接口中定义的方法和Dog类独有的方法。优点:
访问范围更广:
- 可以调用
Dog类中所有的方法(包括接口中的方法和Dog类独有的方法),功能更全面。简单直观:
- 如果只需要使用特定实现类的功能,不涉及接口的多态性,直接使用实现类引用更简单。
缺点:
缺乏多态性:
Dog类型的引用只能指向Dog类型的对象,无法动态替换为其他实现类的对象。如果未来需要用其他实现类替换
Dog,可能需要修改大量代码。耦合度高:
调用时直接依赖具体实现类,不利于代码扩展和维护。
2. 使用接口作为引用类型:
landAnimal d1 = new Dog();**landAnimal d1 = new Dog();特点:
父接口的引用指向子类实现类的实例
d1的编译时类型是landAnimal,而运行时类型是Dog。可以调用
landAnimal接口中定义的方法,即:eat()、run()和sound()。无法直接调用
Dog类中未在接口中定义的其他方法(如果有)。优点:
多态性:
父接口引用可以指向不同的实现类对象,从而通过同一类型的引用调用不同实现类的行为。
例如:
landAnimal d1 = new Dog();,以后可以替换为landAnimal d1 = new Cat();(假如Cat也实现了landAnimal接口),代码的扩展性更强。接口约束:
强制实现类必须实现接口中所有抽象方法,确保代码满足特定的规范。
解耦:
- 调用时依赖接口而非具体实现类,代码更灵活,方便未来替换实现类。
缺点:
方法调用受限
只能调用接口中定义的方法(即
landAnimal、Animal和Runnable中的方法)。无法调用
Dog类中额外定义的方法(如果Dog有独特的方法,无法通过d1直接调用)。
对比总结
对比项 landAnimal d1 = new Dog();Dog d1 = new Dog();引用类型 父接口类型 实现类类型 多态性 支持多态,可以替换为其他实现类 不支持多态,只能引用 Dog类型对象调用范围 只能调用接口中定义的方法 可以调用接口中定义的方法以及 Dog类中的独特方法扩展性 扩展性更强,更适合接口驱动的编程方式 扩展性较差,耦合具体实现类 适用场景 当需要解耦、动态切换不同实现类时适用 当明确只需要特定实现类的功能时适用 灵活性 通过接口实现代码解耦,可替换实现类,便于扩展 强依赖具体实现类,灵活性不足
如何选择?
选择接口引用类型(
landAnimal d1 = new Dog();):
- 需要利用 多态性,让代码可以动态替换不同实现类。
- 编写面向接口的代码,减少对具体实现类的依赖,提高扩展性。
- 场景:有多个实现类(如
Dog、Cat、Horse),且需要动态替换或统一管理它们时。选择实现类引用类型(
Dog d1 = new Dog();):
- 只需使用特定实现类的功能。
- 实现类有特定的方法,且这些方法不会在接口中定义。
- 场景:明确使用
Dog类的特性,不会更换其他实现类时。
总结
- 如果您的程序需要动态替换实现类,或者需要定义松耦合、灵活扩展的代码,优先使用接口作为引用类型。
- 如果您的程序只依赖于特定实现类的功能,且不会涉及接口的多态性,使用实现类作为引用类型更简单直接。
接口和抽象类的区别比较和总结
抽象类和接口的总结
抽象类
抽象类是面向对象编程中用来为其他类提供通用模板的结构。它既可以包含已实现的方法,也可以包含未实现的抽象方法。
特点
-
不能直接实例化:必须通过子类继承后实例化。
-
可以包含构造方法:用于初始化子类会继承的属性。
-
可以包含已实现的方法和抽象方法:既可以定义行为,也可以强制子类实现特定行为。
-
可以定义属性
:
- 实例变量:普通成员变量。
- 静态变量:类级别的共享变量。
-
支持继承:只能单继承,即一个子类只能继承一个抽象类。
-
访问修饰符:可以定义
public、protected和private的方法和变量。
适用场景
- 当多个类之间有共同行为,但这些行为需要在父类中实现或部分实现时使用。
- 提供代码复用的能力。
- 希望类具备部分默认行为的场景。
接口
接口是一个更加抽象的结构,用于定义行为的规范(方法的签名),而不提供具体的实现。类可以实现多个接口,从而实现多继承。
特点
- 不能包含方法实现(Java 8+ 可包含默认方法和静态方法):方法默认为
public abstract。 - 不能定义普通成员变量:只能定义常量(
public static final)。 - 没有构造方法:因为接口不能实例化。
- 支持多继承:一个类可以实现多个接口,一个接口也可以继承多个接口。
- 访问修饰符:所有成员默认是
public。
适用场景
- 定义行为规范(类似于协议或约定),而不提供实现。
- 支持类的多继承。
- 需要解耦合和灵活扩展时使用。
抽象类与接口的比较表
| 特性 | 抽象类 | 接口 |
|---|---|---|
| 实例化 | 不能直接实例化,需通过子类实现 | 不能直接实例化,需通过实现类实现 |
| 构造方法 | 可以包含,用于初始化成员 | 不支持构造方法 |
| 方法 | 既可以包含抽象方法,也可以包含普通方法 | 方法默认为 public abstract,Java 8+ 支持默认和静态方法 |
| 变量 | 可以包含普通变量、静态变量 | 只能包含 public static final 常量 |
| 继承 | 只能单继承,一个子类只能继承一个抽象类 | 支持多继承,一个类可以实现多个接口 |
| 访问修饰符 | 方法和属性可以是 public、protected 或 private |
方法和属性默认为 public |
| 适用场景 | 共享代码、部分行为实现 | 定义行为规范,支持多继承 |
何时使用抽象类与接口?
- 使用 抽象类:
- 当类之间存在 "is-a" 的关系时。
- 希望在父类中提供部分实现,并强制子类实现某些特定行为。
- 需要定义共享的属性和方法时。
- 使用 接口:
- 当类之间没有 "is-a" 的关系,而是 "can-do" 的关系。
- 希望通过接口定义行为规范,且行为的实现可以灵活分配给不同的类。
- 需要实现多继承时。
通过合理使用抽象类和接口,可以提高代码的复用性和扩展性,增强代码的可维护性和灵活性。
复习继承多态对静态、非静态方法的调用
Person p1 =new Student(); 如果在Person Student类中定义了静态方法run() 则 p1.run()是调用哪个
在 Java 中,静态方法是属于类的,而不是属于实例的,所以静态方法的调用不遵循多态性规则。静态方法是根据引用的编译时类型(也就是声明的类型)来决定调用哪个类的静态方法,而不是根据运行时的实际对象类型。
class Person { public static void run() { System.out.println("Person is running."); } } class Student extends Person { public static void run() { System.out.println("Student is running."); } } public class Test { public static void main(String[] args) { Person p1 = new Student(); // 父类引用指向子类实例 p1.run(); // 调用哪个 run() 方法? } }Person is running.原因分析
- 静态方法的绑定规则:
- 静态方法是 类级别 的方法,在编译时就确定了调用哪个类的静态方法。
- 因此,
p1.run()调用的是Person类的run方法,而不是Student类的run方法。- 编译时类型决定调用的静态方法:
- 变量
p1的编译时类型是Person,所以调用的是Person.run()。- 静态方法不支持多态:
- 静态方法不会根据运行时的实际对象类型(这里是
Student)进行动态绑定,静态方法只与变量的编译时类型相关。
扩展:静态方法调用的正确方式
静态方法最好通过类名直接调用,以免造成混淆:
public class Test { public static void main(String[] args) { Person p1 = new Student(); // 推荐直接通过类名调用 Person.run(); // 输出:Person is running. Student.run(); // 输出:Student is running. // 不推荐通过引用调用静态方法,容易引起误解 p1.run(); // 输出:Person is running. } }
总结
- 静态方法是类级别的,不会参与多态。
- 静态方法的调用依据变量的编译时类型,而不是运行时的实际类型。
- 建议通过类名调用静态方法,以避免混淆和误解。
p1调用Person静态方法、Person非静态方法(如果没有被Student重写的话)、Student重写的方法
让我们逐一分析在以下场景中,
p1调用静态方法、非静态方法(没有被重写)以及被Student重写的方法的行为:
代码结构
class Person { // 静态方法 public static void run() { System.out.println("Person is running (static)."); } // 非静态方法(未被子类重写) public void walk() { System.out.println("Person is walking (non-static)."); } // 非静态方法(将被子类重写) public void talk() { System.out.println("Person is talking (non-static)."); } } class Student extends Person { // 静态方法(隐藏父类静态方法) public static void run() { System.out.println("Student is running (static)."); } // 重写父类的非静态方法 @Override public void talk() { System.out.println("Student is talking (non-static)."); } } public class Test { public static void main(String[] args) { Person p1 = new Student(); // 父类引用指向子类实例 // 静态方法调用 p1.run(); // 调用哪个? 父类静态方法run() // 调用父类未被重写的非静态方法 父类没有被重写的方法 walk() p1.walk(); // 调用子类重写的非静态方法 p1.talk(); } }
运行结果
Person is running (static). Person is walking (non-static). Student is talking (non-static).
分析调用行为
1. 调用静态方法:
p1.run()
- 调用结果:
Person is running (static).- 原因:
- 静态方法与变量的编译时类型绑定,而
p1的编译时类型是Person。- 静态方法的调用不受运行时对象的影响,因此调用的是
Person类的静态方法,而不是Student的。
2. 调用父类未被重写的非静态方法:
p1.walk()
- 调用结果:
Person is walking (non-static).- 原因:
walk()是Person的非静态方法,Student没有重写它。- 非静态方法调用会根据运行时对象的实际类型决定执行的版本,而
walk()的唯一版本来自Person。
3. 调用子类重写的非静态方法:
p1.talk()
- 调用结果:
Student is talking (non-static).- 原因:
- 非静态方法支持多态。
- 由于
talk()被Student重写,运行时对象是Student类型,因此调用Student的talk()方法。
总结:调用行为一览表
方法类型 方法在 Person类中方法在 Student类中调用结果 静态方法 run()定义了静态方法 run()定义了静态方法 run()调用编译时类型( Person)的静态方法,结果是Person is running (static).非静态方法 walk()定义了非静态方法 walk()未重写 调用运行时对象的父类版本,结果是 Person is walking (non-static).非静态方法 talk()定义了非静态方法 talk()重写了非静态方法 talk()调用运行时对象的子类版本,结果是 Student is talking (non-static).
注意点
- 静态方法不支持多态:
- 调用的静态方法取决于 引用变量的编译时类型,与运行时实际对象无关。
- 非静态方法支持多态:
- 调用的非静态方法取决于 运行时实际对象的类型。
- 如果子类重写了父类的方法,则调用的是子类版本;否则调用父类版本。
- 推荐静态方法的调用方式:
- 静态方法最好通过类名调用,例如:
Person.run()或Student.run(),避免通过实例变量调用导致混淆。

浙公网安备 33010602011771号