您的位置:首页 > 职场人生

黑马程序员---面向对象二

2013-12-04 18:10 239 查看

继承与多态

----------- android培训java培训、java学习型技术博客、期待与您交流! ------------

1.继承

1.1 概念

定义:是指一个对象直接使用另一对象的属性和方法。

1.1.1 特点

1.使用extends关键字实现;

2.子类可调用父类的非私有方法与属性。

1.1.2 优点

1.提高代码的复用性;

2.使面向对象思想更加直观。

1.1.3 实现

关键字:extends
Java中的继承使用extends关键字实现。

1.1.4 代码实例

需求:学生是人,工人是人,两者都继承自人,都有人的属性与方法

1.编写一个类,人类为父类,有名字,年龄,性别的属性还有吃饭的方法

2.学生、工人继承人类,学生有有自己的学号属性,工人有自己的岗位属性,重写吃饭的方法。

Person类的代码

public abstract class Person {
//父类的公有属性与方法
public String name;
public int age;
public Gender sex;
//抽象方法,本类中不执行但子类若非抽象类则必须执行
public abstract void eat();
//为各个属性赋值的构造方法
public Person(String name , int age , Gender sex){
this.name = name;
this.age = age;
this.sex = sex;
}
public abstract void sayHi();
}
Gneder枚举的代码
//定义枚举,复习一下枚举,使性别只能有男与女两个值
public enum Gender {
男,女;
}

Worker类的代码

public class Worker extends Person {
//特有属性
String work;
public Worker(String name, int age, Gender sex , String work) {
//用父类构造方法为公有属性赋值
super(name, age, sex);
//为专用属性赋值
this.work = work;
// TODO Auto-generated constructor stub
}
//重写父类方法
public void sayHi(){
System.out.println("我的名字是" + super.name + "," + super.sex + ",今年" + super.age +"岁,我的岗位是:" + work );

}
//重写父类方法
public void eat(){
System.out.println("我买盒饭吃!" );
}
}
Student类的代码
public class Student extends Person {
//私有属性
String ID;
public Student(String name, int age, Gender sex , String ID) {
//调用父类构造函数为公有属性赋值
super(name, age, sex);
//为自己的专有属性赋值
this.ID = ID;
}
//重写父类方法
public void sayHi(){
System.out.println("我的名字是" + super.name + "," + super.sex + ",今年" + super.age +"岁,我的学生编号是:" + ID );
}
//重写父类方法
public void eat(){
System.out.println("我在饭堂吃饭!" );
}
}

Test类的代码

public class Test {

/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
//父类指向子类引用
Person p1 = new Student("胡志平",26,Gender.男,"123456");
Person p2 = new Worker("李鹏",19,Gender.男,"技术打样");
//父类对象的数组
Person[]persons = {p1,p2};
//循环输出对象数组中的方法
for (int i = 0; i < persons.length; i++) {
persons[i].sayHi();
persons[i].eat();
}
}
}
输出:
我的名字是胡志平,男,今年26岁,我的学生编号是:123456
我在饭堂吃饭!
我的名字是李鹏,男,今年19岁,我的岗位是:技术打样
我买盒饭吃!


由上代码由看出:继承为我们节省了大量的代码,程序中super关键字、抽象类、方法的重写、父类指向子类引用等知识点就是继承的重点。

1.2 super关键字

1.2.1 this与super

this就是调用本类构造方法,super就是调用父类的构造方法。

1.2.2 super用途

super关键字可以用于调用父类的构造方法。

super关键字可以调用父类的成员

1.2.3 super调用父类构造方法

1.创建一个子类对象时,子类构造方法会在开始执行时,先去调用它的父类的构造方法。

2.若子类的构造函数没有显式调用父类的构造函数,子类默认调用父类无参数构造。

3.子类显式调用父类构造函数

注:

1).在子类构造函数第一行通过super关键字调用父类任何构造函数。

2). 构造函数间的调用只能放在第一行,只能调用一次。

3) super() 和this()不能同时存在构造函数第一行。

1.3 重写(Override)

1.3.1 概念

当子类继承父类,也就继承了父类的方法,有时候,子类需要修改父类中的方法。Java支持这种做法。叫做覆盖(overriding)

案例:测试覆盖

class Father {
String name;
void eat() {
System.out.println("吃红烧肉");
}
}
class Son extends Father {
public void eat() {
// 继承可以使得子类增强父类的方法
System.out.println("吃肯德基");
}
}
class Test {
public static void main(String[] args) {
Son s = new Son();
//执行子类的方法
s.eat();
}
}
注:这就是覆盖

1: 函数名必须相同;

2:参数列表必须相同;

3: 子类重写父类的函数的时候,函数的访问权限必须大于等于父类的函数的访问权限否则编译报错。

4:子类重写父类的函数的时候,返回值类型必须是父类函数的返回值类型或该返回值类型的子类。

注:覆盖(overriding)并不等同重写(Override

覆盖的意思是子类里面写了一个和父类同名的方法重写是子类继承了父类里的虚方法(没有实现的方法)。

不能返回比父类更大的数据类型: 如子类函数返回值类型是Object

1.3.2 重载和重写的不同

1).重载的方法的必须是同一类中的方法,而重写则是类与类之间的而必须有继承关系。

2)重载的函数名相同,参数列表不同; 而重写则要求函数名必须相同,参数列表必须相同,子类返回类型要等于或小于父类返回值类型。

代码实例:

描述不同的吃法

public class Test {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
//父类指向子类引用
//这里不能用父类对象指向子类引用,是因为这种方法适合于父类中公有属性,父类中不输出自己没有的方法和属性。
Student p1 = new Student("胡志平",26,Gender.男,"123456");
p1.eat();
p1.eat("饺子");
}
}
abstract class Person {
//父类的公有属性与方法
public String name;
public int age;
public Gender sex;
public abstract void eat();
//为各个属性赋值的构造方法
public Person(String name , int age , Gender sex){
this.name = name;
this.age = age;
this.sex = sex;
}
public abstract void sayHi();
}
class Student extends Person {
//私有属性
String ID;
public Student(String name, int age, Gender sex , String ID) {
//调用父类构造函数为公有属性赋值
super(name, age, sex);
//为自己的专有属性赋值
this.ID = ID;
}
//重写父类方法
public void sayHi(){
System.out.println("我的名字是" + super.name + "," + super.sex + ",今年" + super.age +"岁,我的学生编号是:" + ID );
}
//重写父类方法
public void eat(){
System.out.println(name + "在饭堂吃饭!" );
}
//重载eat方法
public void eat(String food){
System.out.println("我在饭堂吃" + food);
}
}

:这里不使用Personp1 = new Student()这种形式,是因为这种方法只能输出父类中存在的方法与属性,如果输出p1.eat(“饺子”),则会提示编译错误。

2.多态

2.1 抽象类

2.1.1 概念

当描述一个类的时候,如果不能确定功能函数如何定义,那么该类就可以定义为抽象类,功能函数应该描述为抽象函数。

2.1.2 格式

在class前加上abstract

2.1.3 特点

1:有抽象函数的类,该类一定是抽象类。

2:抽象类中不一定要有抽象函数。

3:抽象类不能使用new创建对象

4:编译器强制子类实现抽象类父类的未实现的方法。

1)可以不实现,前提是子类的也要声明为抽象的。

2.1.4 优点

1:提高代码复用性,强制子类实现父类中没有实现的功能

2:提高代码的扩展性,便于后期的代码维护

2.1.5 疑问

抽象类不能创建对象,那么抽象类中是否有构造函数?

1:抽象类中一定有构造函数。主要为了初始化抽象类中的属性。通常由子类实现。

final和abstract是否可以同时修饰一个类?

不能,因为final是不能继承的意思,而抽象则是反过来,一定要继承。

代码实例

需求:写一个抽象父类(图形类),里面有计算周长与面积的抽象方法, Rect(长方形类)与Circle(类)为其子类实现父类的抽象方法。

public class Test {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Shape s1 = new Rect(5.6,4.5);
Shape s2 = new Circle(6);
Shape [] shapes = {s1,s2};
for (int i = 0; i < shapes.length; i++) {
//复习一下Class对象的知识,判断则是否长方形的对象
if(shapes[i].getClass() == s1.getClass()){
System.out.println("长方形的周长为:" + shapes[i].getLen());
System.out.println("长方形的面积为:" + shapes[i].getArea());
}else{
System.out.println("圆形的周长为:" + shapes[i].getLen());
System.out.println("圆形的面积为:" + shapes[i].getArea());
}
}
}
}
//抽象类里面有两个未实现的抽象方法
abstract class Shape{
public abstract double getLen();
public abstract double getArea();
}
//长方形类
class Rect extends Shape{
double length;
double weigth;
public Rect(double length , double weigth){
this.length = length;
this.weigth = weigth;
}
@Override
public double getLen() {
// TODO Auto-generated method stub

return (length + weigth) * 2;
}
@Override
public double getArea() {
// TODO Auto-generated method stub
return length * weigth;
}
}
//圆形类
class Circle extends Shape{
double radius ;
public Circle(double radius) {
super();
this.radius = radius;
}
@Override
public double getLen() {
// TODO Auto-generated method stub
return 2 * radius * Math.PI;
}
@Override
public double getArea() {
// TODO Auto-generated method stub
return Math.PI * radius * radius;
}
}

2.2 接口

什么是接口
现实中:如USB接口,主要用于扩展电脑的功能。

在java中也是,用于扩展java定义的类的功能。

2.2.1 概念

扩展java定义的类的功能,解决java单继承的缺陷。

2.2.2 定义接口

关键字:interface

2.2.3 格式

interface 接口名{}

2.2.4 使用接口

implements 关键字可以使子类实现指定的多个接口

2.2.5 接口的特点

1.接口中定义的所有属性默认是publicstatic final的,即静态常量(注:即然是静态常量,那么定义的时候必须赋值。既是默认也可以不写public static final)

2.接口中定义的方法不能有方法体,接口中定义的方法默认添加public abstract(注:因为方法默认是public abstract , 所以在定义时可以不写)。

3.接口不可以创建对象,因为接口有抽象方法

4.接口要被子类实现,子类对接口的抽象方法全部覆盖后子类才可以实例化,除非子类也是抽象类或接口。

5.接口可以被多个类实现。

6.如果实现类要访问接口中的成员,直接使用接口名访问即可。

7.接口之间可以多继承,但接口不能实现接口。

8.如果一个类实现了多个接口,那么这个类的对象就是这些接口的对象。

9.如果一个类实现的接口中,有相同的方法,那么则只需要实现一个即可。

问题:接口中有构造函数吗?

没有,因为接口中的元素都是静态常量,而构造函数是用于初始化非静态成员变量的。

2.2.6 抽象类与接口的异同

相同点:两者都有抽象方法。

不同点:

1.功能不同,抽象类是强迫子类重写父类方法,接口是增强实现类功能;

2.抽象类有构造方法,而接口没有;

3.抽象可以有非抽象方法,而接口只能有抽象方法。

实例代码

新建多个接口,每个接口有一个功能family接口中有livingAddress方法,Work接口有Working方法,Play接口中的playGame方法

public class InterfaceDemo {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Person person = new Person();
//Person是这三者的实现子类
if(person instanceof Family){
person.livingAddress();
}
if(person instanceof Work){
person.working();
}
if(person instanceof Paly){
person.palyGame();
}
}
}
//Person类实现其三者的方法
class Person implements Family ,Work,Paly {
@Override
public void palyGame() {
// TODO Auto-generated method stub
System.out.println("我喜欢踢足球");
}
@Override
public void working() {
// TODO Auto-generated method stub
System.out.println("我在电脑城工作");
}
@Override
public void livingAddress() {
// TODO Auto-generated method stub
System.out.println("我住在广州");
}
}
//各个接口
interface Family{
void livingAddress();
}
interface Work{
void working();
}
interface Paly{
void palyGame();
}
输出:
我住在广州
我在电脑城工作
我喜欢踢足球

2.3 内部类

2.3.1 概念

定义在类中的类。

2.3.2 分类

成员内部类,静态内部类(嵌套类),匿名内部类

Outer类中有Inner类 Inner类就是内部类

2.3.3 成员内部类

特点

1.成员内部类和成员变量和成员方法是并列的。

2.成员内部类也是类,也可以定义成员变量和成员方法

3.内部类可以直接访问外部类的成员,而外部类访问内部类则需要通过创建内部类的对象。

4.如果外部类与内部类同时定义一个同名成员(就近原则)。

1) 在内部类中

(1)先找内部类,找不到再找外部类的。

(2)若要访问外部类的,则要通过Outer. this访问其成员(this—>对内部类对象,Outer –-> 外部类)

2)在外部类中

则通过创建内部类的对象来访问。

总结:看到这里其实就应该明白,内部类对象持有有一个所在外部类对象引用.所以在创建内部类对象时,要先创建外部类对象.

public class InnerTest {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
//新建外部类调用其方法
Outer out = new Outer();
out.printNum();
//内部类则需要指定其外部类才可以实例化
Outer.Inner in = out.new Inner();
in.printNum();
}
}
class Outer{
private int x = 1;
private int y = 2;
//成员内部类
class Inner{
int x = 3;
public void printNum(){
System.out.println("x = " + x);
System.out.println("y = " + y);
//同名属性前需要加上外部类的名字
System.out.println("外部类的x = " + Outer.this.x);
}
}
public void printNum(){
System.out.println("x = " + x);
Inner in = new Inner();
//外部类调用内部类则需要先将其实例化
System.out.println("内部类的x = "+in.x);
}
}
输出:
x = 1
内部类的x = 3
x = 3
y = 2
外部类的x = 1

2.4 嵌套内部类(静态内部类)

:被private修饰的成员内部类只能在内部类所有的外部类中创建和访问。

2.4.1 定义

成员内部类被static修饰

2.4.2 特点

1.静态的内部类只能访问静态成员,即即使外部有静态成员变量,但内部类中有同名成员,也不得访问;

2.创建内部类的对象的方式为Outer.Inner = new Outer.Inner()。

class Outer {
int x = 2;
static int y = 1;
// 成员内部类被static修饰
static class Inner {
//静态内部类只能访问外部类的静态成员变量,所以不能用Outer.this访问外部类的同名成员了
int y = 2;
int x = 3;
static int z = 5; // 内部类有静态成员,类也需要是static
void innerPrint() {
System.out.println("内部类的y = " + y); // 2
System.out.println(x); // 3
System.out.println(z);
}
}
void outPrint() {
System.out.println("外部类" + x);
Inner n = new Inner();
n.innerPrint();
}
}
public class InnerTest {
public static void main(String[] args) {
Outer out = new Outer();
// 静态内部类创建方式
Outer.Inner in = new Outer.Inner();
in.innerPrint();
}
}
输出:
内部类的y = 2
x = 3
z = 5

2.5 局部内部类

2.5.1 定义

定义在外部类方法中类。

2.5.2 特点

1.局部内部类访问其所有方法的局部变量,该变量需要定义为final;

2.局部内部类对象只能在该类所有的方法中实例化。

class Outer {
final int z = 10;
int y = 1;
void outPrint() {
// 局部内部类访问局部变量需要使用final修饰
final int y = 2;
class LocalClass {
int x = 3;
void innerPrint() {
System.out.println("局部内部类x = " + x);
System.out.println("外部类中的z = " + z);
System.out.println("方法中的y = " + y);
}
}
System.out.println("外部类y = " + this.y);
LocalClass lc = new LocalClass();
lc.innerPrint();
}
}
public class InnerTest {
public static void main(String[] args) {
Outer out = new Outer();
out.outPrint();
System.out.println();
}
}
输出:
外部类y = 1
局部内部类x = 3
外部类中的z = 10
方法中的y = 2

2.6 匿名内部类

2.6.1 定义

内部类要继承一个类或者实现接口。

2.6.2 特点

1.匿名内部类是内部类的简写格式;

2.其内部类要继承一个类或者实现接口;

3.匿名内部类就是一个匿名子类对象

interface MyInterface {
public static final int x = 3;
public abstract void print();
public abstract void run();
}
class Outer {
int x = 2;
static int y = 1;
void outPrint() {
final int y = 2;
class LocalClass implements MyInterface {
public void print() {
System.out.println("局部内部类。。。。");
}
public void run() {
System.out.println("跑跑。。。");
}
}
LocalClass lc = new LocalClass();
lc.print();
}
}
public class TestInner {
public static void main(String[] args) {
new Outer().outPrint();
System.out.println();
}
}

3.异常处理

3.1 异常概述

在程序中,错误是难以避免的,在Java中在程序运行时可能出现的一些错误称之为异常,异常中断了正在执行的程序的正常运行。

如下例:

public class ArithException {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
int result = 3 / 0;
System.out.println(result);
}
}
输出:
Exception in thread "main" java.lang.ArithmeticException: / by zero
at com.ping.Exception.ArithException.main(ArithException.java:10)

程序运行发生了算术异常ArithExceptio,系统不能继续执行,提前结束。

Java是面向对象的编程语言,因此异常也是作为烦的实例出现。当在某一方法中发生错误时,这个方法创建一个对象,并且把它传递给运行时的系统。这个对象就是异常对象。通过异常处理机制,可以将非正常情况下的处理代码与程序的主逻辑分离,退出在编写代码主流程的同时在其他地方处理异常。

3.2 处理程序异常错误

在Java中,如果某个方法抛出异常,既可以在当前方法中进行捕捉,然后处理该异常,也可以将异常向上抛出,由方法调用者处理。

异常产生后,如果不作任何处理,程序就会被终止。

如上例子。

3.2.1 捕捉异常

Java语言的异常捕捉结构由try、catch、finally3部分组成。其中,try语句存放的是可能发生异常的Java语句;catch程序块在try语句块是try语句之后,用来激发被捕捉的异常;finally语句块是异常处理结构的最后执行部分,无论try块的代码是否执行,都将执行finally块。

语法:

try {
//需要捕捉异常的代码块
} catch (Exception e) {
// TODO: handle exception
//对Exception的处理
}finally{
//代码块
}
由上可知,异常处理器大致分为try-catch语句块与finally语句块。

1.try-catch语句

代码实例,用try-catch捕捉一异常

try {
System.out.println("我的年龄是:" + Integer.parseInt("26L"));
} catch (Exception e) {
// TODO: handle exception
//对Exception的处理
System.out.println("发生异常!");
}
System.out.println("HelloWorld!");
输出:
发生异常!
HelloWorld!
由运行结果可得,程序发生异常,但却没有像往常那样中上程序,而是通过try捕捉,而后catch再处理,最后继续运行剩下的程序。

注:异常处理常用三种函数来获取异常的有关信息。

1)getMessage()函数:出错误性质;

2)toString()函数:给出异常的类型与性质;

3)printStackTrace()函数:指出异常的类型、性质、栈层次及出现在程序中的位置。

2.finally语句

完整的异常处理一定要包含finally语句,无论程序中有无异常,并且是否try-catch是否顺利执行完毕,都会执行finally语句。

但是有4种情况,finally块不会被执行。

1)在finally语句中出现异常;

2)在前面的代码中使用System.exit()退出程序;

3)程序所有线程死亡;

4)关闭CPU.

3.3 Java常见异常

Java中常见的异常如下表:



用户可以自定义异常,用户自定义异常,只需要继承Exception类即可。

3.4 自定义异常

3.4.1 自定义异常类的步骤

1.创建自定义异常类;

2.在方法中通过throw关键字抛出异常对象;

3.如果在当前抛出异常的方法中处理异常,可以使用try-catch语句捕获并处理,否则在方法的声明处通过throws关键字指明要抛出给方法调用者的异常,继续下一步操作;

4.在出现异常方法的调用者是捕获并处理异常。

代码实例

需求:

1.自定义一异常类;

2.定义一测试类,有一个函数,异常在函数处抛出,然后到main()处处理异常。

public class MyExceptionDemo {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
//try代码块处理可能出现异常的代码
try {
//调用avg方法
int result = avg(102,30);
//输出返回值
System.out.println(result);
} catch (MyException e) {
// TODO Auto-generated catch block
//输出错误信息
System.out.println(e);
}
}
static int avg(int num1, int num2) throws MyException{
//判断方法中的参数是否满足条件
if(num1<0 || num2 <0){
//错误信息
throw new MyException("不可以使用负数");
}else if(num1 > 100 || num2 > 100){
throw new MyException("数字太大了");
}
//方法返回值
return (num1 + num2) / 2;
}
}
@SuppressWarnings("serial")
//创建自定义异常,继承Exception
class MyException extends Exception{
//构造方法
public MyException(String ErrorMessage){
//父类构造方法
super(ErrorMessage);
}
}
输出:
com.ping.MyException.MyException: 数字太大了

3.5 方法中抛出异常

若某个方法可能会发生异常,但不想在当前方法中处理这个异常,则可以使用throws、throw关键字在方法抛出异常。

3.5.1 使用throws关键字抛出异常

throws关键字通常被应用在声明方法时,用来指定方法可能抛出的异常。多个异常可使用逗号分隔。

public class ThrowsDemo {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
int result;
// try语句处理异常信息
try {
// 调用方法
result = div(20, 0);
System.out.println("两数相除的结果是:" + result);
// 处理抛出的异常
} catch (ArithmeticException e) {
// 输出异常信息
// TODO Auto-generated catch block
e.printStackTrace();
}
}
static int div(int num1, int num2) throws ArithmeticException {
// 定义方法并抛出ArithmeticException错误
return num1 / num2;
}
}
输出:
java.lang.ArithmeticException: / by zero
at com.ping.MyException.ThrowsDemo.div(ThrowsDemo.java:23)
at com.ping.MyException.ThrowsDemo.main(ThrowsDemo.java:12)
注:使用throws关键字将异常抛给上一级后,如果还是不想处理异常,可以继续向上抛,但最终也是要处理的。

3.5.2 使用throw关键字抛出异常

throw关键字通常用于方法体中,并且抛出一个异常对象。程序在执行到throw语句时立即终止,它后面的语句都不执行。通过throw抛出异常后,如果想在上一级代码中来捕获并处理异常,则需要在抛出异常的方法中使用throws关键字在方法的声明中指明要抛出的异常;如果要捕捉throw抛出的异常,则必须使用try-catch语句。

throw通常用来抛出用户自定义的异常。如下代码:

需求:

1.使用自定义异常类MyException,继承类Exception;

2.使用throw关键字捕捉异常;

3.在main方法中处理异常。

public class MyExceptionDemo02 {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
//捕捉异常
try {
//调用方法
int result = divsion(21,-3);
System.out.println("result = " + result);
} catch (MyException02 e) {
//处理自定义异常
// TODO Auto-generated catch block
System.out.println(e.getMessage());
//处理ArithmeticException异常
}catch(ArithmeticException e){
System.out.println("除数不能为0");
//处理其他异常
}catch(Exception e){
System.out.println("程序其他地方发生异常!");
}
}
//定义抛出的异常
public static int divsion(int x , int y)throws MyException02{
if(y < 0){
//将异常信息转递给自定义异常类
throw new MyException02("除数不能小于0");
}
return x / y;
}
}
@SuppressWarnings("serial")
//创建自定义异常类
class MyException02 extends Exception{
//自定义异常信息
String  message;
public MyException02(String ErrorMessage){
message = ErrorMessage;
}
//覆盖getMessage()方法
public String getMessage(){
return message;
}
}

3.6 运行时异常

RuntimeException异常是程序运行过程产生的异常,Java类库中的每个包中都定义了异常类,所有这些类都是Throwable类的子类。Throwable类派生了两个子类,分别是Exception和Error类。Error类及其子类用来描述Java运行系统中的内部错误以及资源耗尽的错误,这类错误比较严重。Exception类称为非致命性类,可以通过捕捉处理使程序继续执行。Exception类又根据错误发生的原因分为RuntimeException异常和RuntimeException之外的异常。

异常类的结构如下图。



3.7 异常使用原则

1. 在当前方法声明中使用try-catch语句捕获异常;

2.一个方法被覆盖时,覆盖它的方法必须抛出相同的异常或异常的子类;

3.如果父类抛出多个异常,则覆盖方法必须抛出那些异常的一个子集,不能抛出新异常。

----------- android培训java培训、java学习型技术博客期待与您交流! ------------
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: