ChapterFour-面向对象中-三大特性

封装性(Encapsulation)

一、理解封装

  • 封装(Encapsulation)是面向对象的三大特征之一(另外两个是继承和多态),它指的是将对象的状态信息隐藏在对象内部,不允许程序直接访问对象内部信息,而是通过该类所提供的方法来实现对内部信息的操作和访问。
  • 封装是面向对象编程语言对客观世界的模拟,客观世界里的Field都是被隐藏在对象内部的,外界无法直接操作和修改。对一个类或对象实现良好的封装,可以实现以下目的。
  1. 隐藏类的实现细节。
  2. 让使用者只能通过事先预定的方法来访问数据,从而可以在该方法里加入控制逻辑,限制对Field的不合理访问。
  3. 可进行数据检查,从而有利于保证对象信息的完整性。
  4. 便于修改,提高代码的可维护性。
  • 为了实现良好的封装,需要从两个方面考虑。
  1. 将对象的Field和实现细节隐藏起来,不允许外部直接访问。
  2. 把方法暴露出来,让方法来控制对这些Field进行安全的访问和操作。

因此,封装实际上有两个方面的含义:把该隐藏的隐藏起来,把该暴露的暴露出来。这两个方面都需要通过使用Java提供的访问控制符来实现。

二、访问控制符
在这里插入图片描述
使用

  1. 类里的绝大部分Field都应使用private修饰,只有一些static修饰的、类似局部变量的Field,才能考虑使用public修饰。除此之外,有些方法只是用于辅助实现该类的其他方法(比如排序类中定义各种排序算法使用到的交换方法) 这些方法被称为工具方法,工具方法也应该使用private修饰。
  2. 如果某个类主要用做其他类的父类,该类里包含的大部分方法可能仅希望被其子类重写,而不想被外界直接调用,则应该使用protected修饰这些方法。
  3. 希望暴露出来给其他类自由调用的方法应该使用public修饰。因此,类的构造器通过使用public修饰, 从而允许在其他地方创建该类的实例。因此外部类通常都希望被其他类自由使用,所以大部分外部类都使用public修饰。

注意:

  1. java规定的4种权限(从小到大排列):private、缺省(default)、protected、public
  2. 四中权限可以用来修饰类及类的内部结构:属性、方法、构造器、内部类(注意:没有代码块)
  3. 修饰外部类的话,只能使用:缺省、public
    为什么外部类不可以用private和protected修饰?
因为外部类没有处于任何类的内部,也就没有其所在类的内部、所在类的子类两个范围,因此private和protected访问控制符对外部类没有意义 。
/*
 * 面向对象的特征一:封装与隐藏
 * 一、问题的引入:
 * 	当我们创建一个类的对象以后,我们可以通过“对象.属性”的方式,对对象的属性进行赋值。这里,赋值操作要受到
 * 属性的数据类型和存储范围的制约。除此之外,没有其他制约条件。但是,在实际问题中,我们往往需要给属性赋值
 * 加入额外的限制条件。这个条件就不能再属性声明时体现,我们只能通过方法进行限制条件的添加。(比如:setLegs())
 * 同时,我们需要避免用户在使用“对象.属性”的方式对属性进行赋值。则需要将属性声明为私有的(private)
 * --》此时,针对属性就体现了封装性
 * 
 * 二、封装性的目的与实现:
 * 1. 对于一个类或对象实现良好的封装,可以实现以下目的。
 * 	1.隐藏类的实现细节
 * 	2.让使用者只能通过实现预定的方法来访问数据,从而可以在方法里加入控制逻辑,限制对Field
 * 		的不合理访问。
 * 	3.可进行数据检查,从而有利于保证对象信息的完整性。
 * 2.为了实现良好的封装,需要从两个方面考虑。
 *	1.将对象的Field和实现细节隐藏起来,不允许外部直接访问。
 *	2.把方法暴露出来,让方法来控制对这些field进行安全的访问和操作
 *	因此:封装实际上有两方面的含义:把该隐藏的隐藏起来,把该暴露的暴露出来。这两个
 *方面都需要通过使用java提供的访问控制符来实现。
 *
 * 三、封装性的体现:
 * 我们将类的属性xxx私有化(private),同时提供公共的(public)方法来获取(getXxx)和设置(setXxx)此属性的值
 * 
 * 	拓展:封装性的体现:①如上 ②不对外暴露私有方法 ③ 单例模式 ...
 * 
 * 四、封装性的体现,需要权限修饰符来配合
 * 1.java规定的4种权限(从小到大排列):private、缺省(default)、protected、public
 * 2.4中权限可以用来修饰类及类的内部结构:属性、方法、构造器、内部类(注意:没有代码块)
 * 3.具体的,4种权限都可以用来修饰类的内部结构:属性、方法、构造器、内部类(注意:没有代码块)
 * 			修饰外部类的话,只能使用:缺省、public
 * 	 3.1 为什么外部类不可以用private和protected修饰?
 * 		因为外部类没有处于任何类的内部,也就没有其所在类的内部、所在类的子类两个范围,
 * 		因此private和protected访问控制符对外部类没有意义
 * 		
 * 总结封装性:Java提供了4种权限修饰符来修饰类及类的内部结构,体现类及类的内部结构在被调用时的可见性的大小
 * 
 * 关于访问控制符的使用,存在如下几条原则。
 *  1.类里的绝大部分Field都应使用private修饰,只有一些static修饰的、类似局部变量的Field,才能考虑使用
 *    public修饰。除此之外,有些方法只是用于辅助实现该类的其他方法(比如排序类中定义各种排序算法使用到的交换方法)
 *    这些方法被称为工具方法,工具方法也应该使用private修饰。
 *    
 *  2.如果某个类主要用做其他类的父类,该类里包含的大部分方法可能仅希望被其子类重写,而不
 *    想被外界直接调用,则应该使用protected修饰这些方法。
 *    
 *  3.希望暴露出来给其他类自由调用的方法应该使用public修饰。因此,类的构造器通过使用public修饰,
 *    从而允许在其他地方创建该类的实例。因此外部类通常都希望被其他类自由使用,所以大部分外部类
 *    都使用public修饰
 *    
 *   小知识:如果一个java源文件里定义的所有类都没有使用public修饰,则这个Java源文件的
 *   文件名可以是一切合法的文件名;但如果一个Java源文件里定义了一个public修饰的类,
 *   则这个源文件的文件名必须与public修饰的类类名相同。
 * 
 */

class Animal{
	
	String name;
	private int age;
	private int legs;//腿的个数
	
	//对属性的设置
	public void setLegs(int l){
		if(l >=0 && l %2 ==0){
			legs=l;
		}else{
			legs=0;
		}
	}
	
	//对属性的获取
	public int getLegs(){
		return legs;
	}
	
	public void eat(){
		System.out.println("动物进食");
	}
	
	public void show(){
		System.out.println("name="+name+",age="+age+",legs="+legs);
	}
	
	//提供关于属性age的set和get方法
	public int getAge(){
		return age;
	}
	public void setAge(int a){
		age=a;
	}
}

//Illegal modifier for the class Dog; only public, abstract & final are permitted
//private class Dog{
//	
//}
public class AnimalTest {
	
	public static void main(String[] args) {
		
		Animal test= new Animal();
		//test.age=3;//The field Animal.age is not visible
		//test.legs;
		test.name="大黄";
//		test.legs;- The field Animal.legs is not visible
		test.setLegs(6);
		
		test.show();//name=大黄,age=0,legs=6
		System.out.println(test.name);//大黄
	}
}

继承性(Inheritance)

1.为什么要有继承

  • 多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类无须再定义这些属性和行为,只要继承那个类即可。

2.继承的好处

  1. 减少了代码的冗余,提高了代码的复用性
  2. 便于功能的扩展
  3. 为之后多态的使用,提供了前提

3.继承的语法格式

* 二、继承的格式:
 * 	class A extends B{}
 * 	A:子类、派生类、subclass
 * 	B:父类、超类、基类、superclass
 * 
 * 	2.1体现:一旦子类A继承父类B以后,子类A中就获取了父类B中声明的所有的属性和方法(不能获得父类的构造器)
 * 		特别地,父类中声明为private的属性或方法,子类继承父类以后,仍然会获取父类中的私有的结构。
 * 		只是因为封装性的影响,使得子类不能直接调用父类的结构而已。
 *  2.2 子类继承父类以后,还可以声明自己特有的属性或方法:实现功能的拓展。
 *  子类和父类的关系,不同于子集和集合的关系。
 *  extends:延展、扩展

4.Java中关于继承性的规定

  • 一个类可以被多个子类继承
  • Java中类的单继承性:一个类只能有一个父类(严格来讲,这种说法是错误的,应该换成如下说法:Java类只能有一个直接父类,实际上,Java类可以有无限多个间接父类。例如:如下代码:上面的类定义中Fruit是Apple类的父类,Plant类也是Apple类的父类。区别是Fruit是Apple的直接父类,而Plant则是Apple类的间接父类)

class Fruit extends Plant{…}
class Apple extends Fruit{…}

在这里插入图片描述
在这里插入图片描述

  • 子父类是相对的概念
  • 子类继承父类以后,就获取了直接父类以及所有间接父类中声明的属性和方法(包括私有的属性和方法,只是受到封装性的影响可能无法直接调用但是没有构造器)
  • 如果我们没有显式的声明一个类的父类的话,则此类继承于java.lang.Object类
  • 所有的java类(除java.lang.Object类之外)都直接或间接的继承于java.lang.Object类
  • 意味着,所有的java类具有java.lang.Object类声明的功能。
package com.liuyongbin.extendS;
/*
 * 面向对象的特征之二:继承性        why?
 * 
 * 一、继承性的好处:
 * ①减少了代码的冗余,提高了代码的复用性
 * ②便于功能的扩展
 * ③为之后多态的使用,提供了前提
 * 
 * 二、继承的格式:
 * 	class A extends B{}
 * 	A:子类、派生类、subclass
 * 	B:父类、超类、基类、superclass
 * 
 * 	2.1体现:一旦子类A继承父类B以后,子类A中就获取了父类B中声明的所有的属性和方法(不能获得父类的构造器)
 * 		特别地,父类中声明为private的属性或方法,子类继承父类以后,仍然会获取父类中的私有的结构。
 * 		只是因为封装性的影响,使得子类不能直接调用父类的结构而已。
 *  2.2 子类继承父类以后,还可以声明自己特有的属性或方法:实现功能的拓展。
 *  子类和父类的关系,不同于子集和集合的关系。
 *  extends:延展、扩展
 *  
 *  三、Java中关于继承性的规定:
 *  	1.一个类可以被多个子类继承
 *  	2.Java中类的单继承性:一个类只能有一个父类(严格来讲,这种说法是错误的,应该换成如下说法:Java类
 *  	只能有一个直接父类,实际上,Java类可以有无限多个间接父类。例如:
 *  	class Fruit extends Plant{...}
 *  	class Apple extends Fruit{...}
 *  	上面的类定义中Fruit是Apple类的父类,Plant类也是Apple类的父类。区别是Fruit是Apple的直接父类,而
 *  	Plant则是Apple类的间接父类)
 *  	3.子父类是相对的概念
 *  	4.子类继承父类以后,就获取了直接父类以及所有间接父类中声明的属性和方法
 * 
 *  四、 1. 如果我们没有显式的声明一个类的父类的话,则此类继承于java.lang.Object类
 *    	2. 所有的java类(除java.lang.Object类之外)都直接或间接的继承于java.lang.Object类
 *    	3. 意味着,所有的java类具有java.lang.Object类声明的功能。
 */
 class Person {
	
	String name;
//	 int age;
	private int age;
	
	public Person(){
		
	}
	
	public Person(String name,int age){
		this.name=name;
		this.age=age;
	}
	
	public void eat(){
		System.out.println("吃饭");
	}
	
	public void sleep(){
		System.out.println("睡觉");
	}
	
	public void setAge(int age){
		this.age=age;
	}
	
	public int getAge(){
		return this.age;
	}
	
}

class Student extends Person{


	String major;
	
	public Student(){
		
	}
	
	public Student(String name,int age,String major){
		this.name=name;
//		this.age=age;
		setAge(age);//封装性的影响,不能直接调用,间接通过set方法调用
		this.major=major;
	}
	
	public void study(){
		System.out.println("学习");
	}
	
	public void show(){
		System.out.println("名字:"+this.name+",年龄"+this.getAge());
	}
	
}

public class ExtendsTest {
	public static void main(String[] args) {
		
		
		//把student中本身没有name、age、eat()、sleep()
		Student st= new Student();
		//因为Person父类里有name、age、eat()、sleep()
		st.name="刘瘦瘦";
//		st.age=12;(下面的测试把这个声明为私有化了)
		st.eat();
		st.sleep();
		st.show();
		
		System.out.println("----------------------");
		//测试父类中私有的方法或属性会不会被子类所继承,我们把age属性和eat方法声明为私有的
		//那么在子类中的age就会报错,因为父类的age声明private就只能在当前类使用,所以我们通过
		//在父类中对age提供get和set方法来测试子类中是否具有私有的属性或方法,测试可以通过
		//说明子类继承了父类所有的方法和属性
		st.setAge(6);
		st.getAge();
		st.show();			
	}

5.方法的重载
方法的重载
6.super关键字
查看super关键字
7. 子类对象实例化的全过程

  1. 从结果上看:继承性
  • 子类继承父类以后,就获取了父类中声明的属性或方法。
  • 创建子类的对象,在堆空间中,就会加载所父类中声明的属性。
  1. 从过程上看:
  • 当我们通过子类的构造器创建子类对象时,我们一定会直接或间接的调用其父类的构造器,进而调用父类的构造器,…直到调用了java.lang.Object类中空参的构造器为止。正因为加载过所的父类的结构,所以才可以看到内存中父类中的结构,子类对象才可以考虑进行调用?(图中小方框都是构造器)。

在这里插入图片描述

  1. 虽然创建子类对象时,调用了父类的构造器,但是自始至终就创建过一个对象,即为new的子类对象。

在这里插入图片描述
小结:Java中的继承机制使得一个类可以继承另一个类,继承的类称为子类,被继承的类称为父类。在一个子类被创建的时候,首先会在内存中加载父类的构造器,包括直接父类和间接父类直至object的空参构造器。正因为加载过所有父类的结构,所以才可以看到内存中有父类的结构:所有的属性和方法(包括私有的)只是受到封装性的影响,我们不能直接调用父类中私有的属性或方法,但是我们可以通过间接调用。比如对于私有的属性我们可以提供set/get方法来调用,对于私有的方法,我们也可以把私有的方法放在另一个公开的方法中,通过调用另一个公开的方法来调用私有的方法。

class Person{
	private String name;
	private int id;
	
	public Person(){
		
	}
	
	public void function(){
		eat();
		sleep();
	}
	
	private void eat(){
		System.out.println("人可以睡觉");
	}
	
	public void sleep(){
		System.out.println("人必须睡觉");
	}
}

class Student extends Person{
	
	public void sleep(){
		System.out.println("学生要努力学习,少睡觉");
	}
	
}
public class Test {
	
	public static void main(String[] args) {
		
		Student st=new Student();
		st.function();
		/*
		 * 输出结果:
		 * 人可以睡觉
			学生要努力学习,少睡觉
		 */
		//可以输出父类private修饰的eat()方法,说明子类确实存在父类声明的private属性和方法
	}
}

多态性(Polymorphism)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述在这里插入图片描述
在这里插入图片描述
方法重载可以称为编译时多态。根据实际参数的数据类型、个数和次序,Java在编译时能够确定执行重载方法中的哪一个。
方法重写表现为两种多态性,当对象引用本类实例时,为编译时多态,否则对象引用子类实例时为运行时多态。
在这里插入图片描述

代码演示

package com.liuyongbin.polymorphism;
/*
 * 面向对象特征之三:多态性
 * 
 * 1.理解多态性:可以理解为一个事物的多种形态
 * 2.何为多态性:
 * 			父类的引用指向子类的对象(当运行时调用父类引用变量的方法时,其方法行为总是表现出子类方法的行为特征,而不是父类方法的行为特征,这就
 * 			可能出现:相同类型的变量、调用同一个方法时呈现出多种不同的行为特征,这就是多态)
 *  补充知识:
 *  Java引用类型变量有两个类型:编译时类型和运行时类型。编译时类型由声明该变量时使用的类型决定,
 * 	运行时类型由实际赋给变量的对象决定。如果编译时类型和运行时类型不一致,就可能出现所谓的多态
 * 
 * 3.多态的使用:虚拟方法调用
 *    有了对象的多态以后,我们在编译期,只能调用父类中声明的方法,但在运行期,我们实际执行的是子类重写父类的方法。
 * 	      简称:编译时,看左边;运行时,看右边。
 * 		
 * 4.多态的使用前提:①类的继承关系 ②方法的重写(我们有多态就是为了完成不同的功能,如果不重写方法不如直接声明父类对象,直接调用方法即可)
 * 
 * 5.对象的多态性,只适用于方法,不适用与属性(编译和运行都看左边)
 * 
 */

 class Person {

	String name;
	int age;
	
	int id=1001;
	public void eat(){
		System.out.println("人:吃饭");
	}
	
	public void walk(){
		System.out.println("人:走路");
	}
}


 class Man extends Person {

	boolean isSmoking;
	int id=1002;
	
	public void earnMoney(){
		System.out.println("男人负责挣钱养家");
	}
	
	public void eat(){
		System.out.println("男人多吃肉,长肌肉");
	}
	
	public void walk(){
		System.out.println("男人霸气的走路");
	}
}


 class Woman extends Person {
	
	boolean isBeauty;
	
	public void goShopping(){
		System.out.println("女人喜欢购物");
	}
	
	public void eat(){
		System.out.println("女人少吃,为了减肥");
	}
	
	public void walk(){
		System.out.println("女人窈窕的走路");
	}
}

public class PersonTest {

		public static void main(String[] args) {
			
			//编译时类型和运行时类型完全相同,不构成多态
			Person p1=new Person();
			p1.eat();//人:吃饭
			
			//编译时类型和运行时类型完全相同,不构成多态
			Man man=new Man();
			man.eat();//男人多吃肉,长肌肉
			man.age=25;
			man.earnMoney();//男人负责挣钱养家
			
			//********************
			System.out.println("**********");
			//对象的多态性:父类的引用指向子类的对象
			Person p2=new Man();
			Person p3=new Woman();
			//多态的使用:当调用子父类同名同参的方法时,实际执行的是子类重写父类的方法---虚拟方法调用
			
			p2.eat();//男人多吃肉,长肌肉
			p2.walk();//男人霸气的走路
			
			//我们在编译期,只能调用父类中声明的方法
//			p2.money();
			
			System.out.println(p2.id);//1001
		}
}

package com.liuyongbin.polymorphism;

import java.sql.Connection;

class Animal{
	
	public void eat(){
		System.out.println("动物:进食");
	}
	
	public void shout(){
		System.out.println("动物:叫");
	}
}

class Cat extends Animal{
	public void eat(){
		System.out.println("猫吃鱼");
	}
	
	public void shout(){
		System.out.println("喵喵喵");
	}
}

class Dog extends Animal{
	public void eat(){
		System.out.println("狗吃骨头");
	}
	
	public void shout(){
		System.out.println("汪!汪!汪!");
	}
	
	public void watchDoor(){
		System.out.println("看门");
	}
}

public class PolymorphismExample {
	
	
	public void func(Animal animal){//Animal animal = new Dog();
		animal.eat();
		animal.shout();
		
//		if(animal instanceof Dog){
//			Dog d = (Dog)animal;
//			d.watchDoor();
//		}
	}
	
	//使用多态的话,就不用写这些重载的方法了
	//	public void func(Dog dog){
	//	dog.eat();
	//	dog.shout();
	//}
	//public void func(Cat cat){
	//	cat.eat();
	//	cat.shout();
	//}
	
	public static void main(String[] args) {
		
		PolymorphismExample pe=new PolymorphismExample();
		pe.func(new Dog());
		pe.func(new Cat());
	}
}

//举例三:
class Driver{
	
	//比如我三步连接数据库,Connection中就定义了三个方法来实现连接数据库,
	//那么数据库厂商就必须按照我们Connection中的方法来实现(通过继承Connection来覆写这三个方法)
	//这样我们就可以统一把形参设置成Connection类型,运行时去调用数据库厂家所实现的方法。
	public void doData(Connection conn){//conn = new MySQlConnection(); / conn = new OracleConnection();
		//规范的步骤去操作数据
//		conn.method1();
//		conn.method2();
//		conn.method3();
		
	}
	
}

练习题
1.

/*
 * 子类继承父类
若子类重写了父类方法,就意味着子类里定义的方法彻底覆盖了父类里的
同名方法,系统将不可能把父类里的方法转移到子类中。--->编译看左边,运行看右边
对于实例变量则不存在这样的现象,即使子类里定义了与父类完全相同的
实例变量,这个实例变量依然不可能覆盖父类中定义的实例变量。--->编译、运行都是右边
 */
class Base {
	int count = 10;
	public void display() {
		System.out.println(this.count);
	} 	
}

class Sub extends Base{
	int count=20;
	public void display(){
		System.out.println(this.count);
	}
}



public class FieldMethodTest {
	public static void main(String[] args){
		Sub s = new Sub();
		System.out.println(s.count);//20 对于同名变量的调用:先看方法的声明中是否存在局部变量
		//在看本类中的成员变量是否存在再去看父类中的成员变量是否存在
		s.display();//调用子类重写的方法//20
		Base b = s;//发生了多态
		System.out.println(b == s);//==比较地址值,结果为true 由于是同一个对象 ,只是对象的地址值赋给了不同的引用变量
		System.out.println(b.count);//属性没有多态调用父类的 输出10
		b.display();//调用子类重写的方法
		}
}

2、
在这里插入图片描述
父类GeometricObject声明如下:

public class GeometricObject {//几何图形

	protected String color;
	protected double weight;
	public String getColor() {
		return color;
	}
	public void setColor(String color) {
		this.color = color;
	}
	public double getWeight() {
		return weight;
	}
	public void setWeight(double weight) {
		this.weight = weight;
	}
	public GeometricObject(String color, double weight) {
		super();
		this.color = color;
		this.weight = weight;
	}
	
	public double findArea(){
		return 0.0;
	}
	
}

子类Circle的声明如下:

public class Circle extends GeometricObject {

	private double radius;
	
	public Circle(double radius,String color, double weight) {
		super(color, weight);
		this.radius = radius;
	}

	public double getRadius() {
		return radius;
	}

	public void setRadius(double radius) {
		this.radius = radius;
	}
	
	public double findArea(){
		return 3.14 * radius * radius;
	}

}

子类MyRectangle的声明如下:

public class MyRectangle extends GeometricObject {

	private double width;
	private double height;
	
	public MyRectangle(double width,double height,String color, double weight) {
		super(color, weight);
		this.width = width;
		this.height = height;
	}

	public double getWidth() {
		return width;
	}

	public void setWidth(double width) {
		this.width = width;
	}

	public double getHeight() {
		return height;
	}

	public void setHeight(double height) {
		this.height = height;
	}

	@Override
	public double findArea() {
		return width * height;
	}
}

测试类:GeometricTest测试类声明如下:

/*
 * 
 * 定义一个测试类GeometricTest,
 * 编写equalsArea方法测试两个对象的面积是否相等(注意方法的参数类型,利用动态绑定技术),
 * 编写displayGeometricObject方法显示对象的面积(注意方法的参数类型,利用动态绑定技术)。

 */
public class GeometricTest {

	public static void main(String[] args) {
		GeometricTest test = new GeometricTest();
		
		Circle c1 = new Circle(3.3, "white", 1.0);
		test.displayGeometricObject(c1);
		Circle c2 = new Circle(3.3, "white", 1.0);
		test.displayGeometricObject(c2);
		
		boolean isEquals = test.equalsArea(c1, c2);
		System.out.println("c1和c2的面积是否相等" + isEquals);
		
		MyRectangle rect = new MyRectangle(2.1, 3.4, "red", 2.0);
		test.displayGeometricObject(rect);
		
	}
	
	public void displayGeometricObject(GeometricObject o){//GeometricObject o = new Circle(...)
		System.out.println("面积为" + o.findArea());
	}
	
	//测试两个对象的面积是否相等
	public boolean equalsArea(GeometricObject o1,GeometricObject o2){
		return o1.findArea() == o2.findArea();
	}
}


多态中的面试题
面试题

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值