java学习第五章-java面向对象(仅做个人学习笔记用)

5.1.1 继承的实现

 面向对象的三大特征:继承、封装、多态

 继承让我们更加容易实现类的扩展。 比如,我们定义了人类,再定义Boy类就只需要扩展人类即可。实现了代码的重用,不用再重新发明轮子(don’t  reinvent  wheels)。

从英文字面意思理解,extends的意思是“扩展”。

 在我们编程中,如果新定义一个Student类,发现已经有Person类包含了我们需要的属性和方法,那么Student类只需要继承Person类即可拥有Person类的属性和方法。

package cn.nls.oo2;

/**
 * 测试继承
 * @author 
 *
 */

public class TestExtends {
	public static void main(String[] args) {
		Student stu = new Student();
		stu.name="大哥";
		stu.height=177;
		stu.rest();//休息一会!
		
		Student stu2 = new Student("小弟",222,"高数");
		
		/*测试instanceof*/
		System.out.println(stu2 instanceof Student);//true
		System.out.println(stu2 instanceof Person);//true
		System.out.println(stu2 instanceof Object);//true
		System.out.println(new Person() instanceof Student);//false
	}

}


class Person /*extends Object*/{
	String name;
	int height;
	
	public void rest() {
		System.out.println("休息一会!");
	}
}

class Student extends Person{
	//String name;
	//int height;
	String major;
	
	public void study() {
		System.out.println("学习两小时!");
	}
	
	//public void rest() {
	//	System.out.println("休息一会!");
	//}
	
	public Student(String name,int height,String major) {
		this.name = name;
		this.height= height;
		this.major = major;
	}
	
	public Student() {
		
	}
}

instanceof是二元运算符,左边是对象,右边是类;当对象是右面类或子类所创建对象时,返回true;否则,返回false。 

 5.1.3 继承使用要点

可以使用ctrl+T方便的查看类的继承层次结构

1.父类也称作超类、基类、派生类等。

2.Java中只有单继承,没有像C++那样的多继承。多继承会引起混乱,使得继承链过于复杂,系统难于维护。

3.Java中类没有多继承,接口有多继承

4.子类继承父类,可以得到父类的全部属性和方法 (除了父类的构造方法),但不见得可以直接访问(比如,父类私有的属性和方法)。

5.如果定义一个类时,没有调用extends,则它的父类是:java.lang.Object

5.1.4 方法的重写override

子类通过重写父类的方法,可以用自身的行为替换父类的行为。方法的重写是实现多态的必要条件。

方法的重写需要符合下面的三个要点:

      1.“==”: 方法名、形参列表相同。

      2.“≤”:返回值类型和声明异常类型,子类小于等于父类。

      3.“≥”: 访问权限,子类大于等于父类。

package cn.nls.oo2;


/**
 * 测试重写(override)/覆盖
 * @author 
 *
 */
public class TestOverride {
	public static void main(String[] args) {
		Horse h = new Horse();
		h.run();
	}

}

class Vehicle{
	public void run() {
		System.out.println("跑....");
	}
	
	public void stop() {
		System.out.println("停止!");
	}
	
	public Person whoIsPsg() {
		return new Person();
	}
}

class Horse extends Vehicle{
	public void run() {//重写
		System.out.println("四蹄翻飞,嘚嘚嘚。。。");
	}
	
	public Student whoIsPsg() {//重写,返回类型student,比Person小,即小于父类类型,可以
		return new Student();
	}
	
}

5.2.1 Object

Object类是所有Java类的根基类,也就意味着所有的Java对象都拥有Object类的属性和方法。如果在类的声明中未使用extends关键字指明其父类,则默认继承Object类。

package cn.nls.oo2;

public class TestObject {
	public static void main(String[] args) {
		//Object obj;
		
		TestObject to = new TestObject();
		System.out.println(to.toString());
		
		Person2 p2 = new Person2("狗子",66);
		System.out.println(p2.toString());
	}
	
	public String toString() {//toString的重写
		return "测试Object对象";
	}

}

class Person2{
	String name;
	int age;
	
	public String toString() {
		return name+",年龄:"+age;
	}
	
	public Person2(String name,int age) {
		this.name = name;
		this.age = age;
	}
}

5.2.3 ==和equals方法

==”代表比较双方是否相同。如果是基本类型则表示相等,如果是引用类型则表示地址相等即是同一个对象

Object类中定义有:public boolean equals(Object obj)方法,提供定义“对象内容相等”的逻辑。比如,我们在公安系统中认为id相同的人就是同一个人、学籍系统中认为学号相同的人就是同一个人。

Object 的 equals 方法默认就是比较两个对象的hashcode,是同一个对象的引用时返回 true 否则返回 false。但是,我们可以根据我们自己的要求重写equals方法。

package cn.nls.oo2;

public class TestEquals {
	public static void main(String[] args) {
		Object obj;
		String str;
		
		User u1 = new User(1999,"高琪","123456");
		User u2 = new User(1999,"高息","123456");
		
		System.out.println(u1==u2);//false,false
		System.out.println(u1.equals(u2));//false,重写后true
		
		String str1 = new String("sxt");
		String str2 = new String("sxt");
		System.out.println(str1==str2);//false
		System.out.println(str1.equals(str2));//true
	}

}


class User{
	int id;
	String name;
	String pwd;

	public User(int id, String name, String pwd) {
		super();
		this.id = id;
		this.name = name;
		this.pwd = pwd;
	}
	
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + id;
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		User other = (User) obj;
		if (id != other.id)
			return false;
		return true;
	}
	
	
}

  JDK提供的一些类,如String、Date、包装类等,重写了Object的equals方法,调用这些类的equals方法, x.equals (y) ,当x和y所引用的对象是同一类对象且属性内容相等时(并不一定是相同对象),返回 true 否则返回 false。

5.3 super关键字

 super是直接父类对象的引用。可以通过super来访问父类中被子类覆盖的方法或属性。

 使用super调用普通方法,语句没有位置限制,可以在子类中随便调用。

若是构造方法的第一行代码没有显式的调用super(...)或者this(...);那么Java默认都会调用super(),含义是调用父类的无参数构造方法。这里的super()可以省略。

package cn.nls.oo2;

public class TestSuper01 {
	public static void main(String[] args) {
		new ChildClass().f();
	}

}

class FatherClass {
	public int value;
	public void f() {
		value = 100;
		System.out.println("FatherClass.value="+value);//FatherClass.value=100
	}
}

class ChildClass extends FatherClass{
	public int value;
	public void f() {
		super.f();//调用父类对象的普通方法
		value = 200;
		System.out.println("ChildClass.value="+value);//ChildClass.value=200
		System.out.println(value);//200
		System.out.println(super.value);//100,调用父类对象的成员变量
	}
}

 

构造方法调用顺序:

      构造方法第一句总是:super(…)来调用父类对应的构造方法。所以,流程就是:先向上追溯到Object,然后再依次向下执行类的初始化块和构造方法,直到当前子类为止。

      注:静态初始化块调用顺序,与构造方法调用顺序一样,不再重复。

package cn.nls.oo2;

public class TestSuper02 {
	public static void main(String[] args) {
	System.out.println("开始创建一个ChildClass对象....");
	new ChildClass02();
	}

}

class FatherClass02{
	public FatherClass02() {
		System.out.println("创建FatherClass02");
	}
}

class ChildClass02 extends FatherClass02{
	public ChildClass02() {
		//super();
		System.out.println("创建ChildClass02");
	}
}

5.4封装 

我们程序设计要追求“高内聚,低耦合”。 高内聚就是类的内部数据操作细节自己完成,不允许外部干涉;低耦合是仅暴露少量的方法给外部使用,尽量方便外部调用。

编程中封装的具体优点:

     1. 提高代码的安全性。

     2. 提高代码的复用性。

     3. “高内聚”:封装细节,便于修改内部代码,提高可维护性。

     4. “低耦合”:简化外部调用,便于调用者使用,便于扩展和协作。

5.4.2 封装的实现-使用访问控制符

Java是使用“访问控制符”来控制哪些细节需要封装,哪些细节需要暴露的。 Java中4种“访问控制符”分别为private、default、protected、public,它们说明了面向对象的封装性,所以我们要利用它们尽可能的让访问权限降到最低,从而提高安全性。

下面详细讲述它们的访问权限问题。其访问权限范围如表5-1所示。

表5-1 访问权限修饰符

图1.png

1. private 表示私有,只有自己类能访问

2. default表示没有修饰符修饰,只有同一个包的类能访问

3. protected表示可以被同一个包的类以及其他包中的子类访问

4. public表示可以被该项目的所有包中的所有类访问

代码1:

package cn.nls.oo2;

/**
 * 测试封装
 * @author 
 *
 */

public class TestEncapsulation {
	public static void main(String[] args) {
		Human h = new Human();
		//h.age=10;//age不可见
		h.name = "高琪";
		h.Height = 8;
		
	}

}
/*
class Human{
	private int age;
	String name;
	
	void sayAge() {
		System.out.println(age);
	}
}*/

class Boy extends Human{
	void sayHello() {
		//System.out.println(age);//报错,子类无法使用父类的私有属性和方法
	}
}

代码2:

package cn.nls.oo;

import cn.nls.oo2.Human;

public class TestEncapsulation2 {
	public static void main(String[] args) {
		Human h = new Human();
		//h.age=10;//age不可见
		//h.name = "高琪"; //name为default属性,不能被不同包的类访问
		//h.Height = 8;//protected属性,同包可见,不同包不可见
		h.sayAge();
	}

}

class Girl extends Human{
	void sayGood() {
		System.out.println(Height);
	}
}

代码3:

package cn.nls.oo2;

public class Human {
	private int age;
	String name;  //,默认default,可以被本包下面的类访问
	protected int Height;
	
	public void sayAge() {
		System.out.println(age);
	}

}

 5.4.3 封装的使用细节

 

类的属性的处理:

      1. 一般使用private访问权限。

      2.  提供相应的get/set方法来访问相关属性,这些方法通常是public修饰的,以提供对属性的赋值与读取操作(注意:boolean变量的get方法是is开头!)。

      3. 一些只用于本类的辅助性方法可以用private修饰,希望其他类调用的方法用public修饰。

javaBean封装实例

package cn.nls.oo2;

/**
 * 仍然测试测试封装
 * @author 
 *
 */
public class Person4 {
	private int id;
	private String name;
	private int age;
	private boolean man;
	
	public void setName(String name) {
		this.name = name;
	}
	public String getName() {
		return this.name;
	}
	
	public void setAge(int age) {
		if(age>=1&&age<=130) {
			this.age = age;
		}else {
			System.out.println("请输入正常的年龄!");
		}
	}
	public int getAge() {
		return this.age;
	}
	
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public boolean isMan() {
		return man;
	}
	public void setMan(boolean man) {
		this.man = man;
	}

	
}

5.5 多态 (polymorphism)

多态指的是同一个方法调用,由于对象不同可能会有不同的行为。

多态的要点:

      1. 多态是方法的多态,不是属性的多态(多态与属性无关)。

      2. 多态的存在要有3个必要条件:继承,方法重写,父类引用指向子类对象。

      3. 父类引用指向子类对象后,用该父类引用调用子类重写的方法,此时多态就出现了。

package cn.nls.oo2;
/**
 * 测试多态
 * @author 
 *
 */
public class TestPolym {
	public static void main(String[] args) {
		Animal a = new Animal();
		animalCry(a);//叫了一声!
		Dog d = new Dog();
		animalCry(d);//旺旺旺
	}
	
	static void animalCry(Animal a) {
		a.shout();
	}

}

class Animal{
	public void shout() {
		System.out.println("叫了一声!");
	}
}

class Dog extends Animal{
	public void shout() {
		System.out.println("旺旺旺");
	}
}

class Cat extends Animal{
	public void shout() {
		System.out.println("喵喵喵!");
	}
}

展示了多态最为多见的一种用法,即父类引用做方法的形参,实参可以是任意的子类对象,可以通过不同的子类对象实现不同的行为方式。

由此,我们可以看出多态的主要优势是提高了代码的可扩展性,符合开闭原则。但是多态也有弊端,就是无法调用子类特有的功能,比如,我不能使用父类的引用变量调用Dog类特有的seeDoor()方法。

 5.6 对象的转型(casting)

 

父类引用指向子类对象,我们称这个过程为向上转型,属于自动类型转换。

      向上转型后的父类引用变量只能调用它编译类型的方法,不能调用它运行时类型的方法。这时,我们就需要进行类型的强制转换,我们称之为向下转型!

在向下转型过程中,必须将引用变量转成真实的子类类型(运行时类型)否则会出现类型转换异常ClassCastException。

package cn.nls.oo2;
/**
 * 测试多态
 * @author
 *
 */
public class TestPolym {
	public static void main(String[] args) {
		Animal a = new Animal();
		animalCry(a);//叫了一声!
		Dog d = new Dog();
		animalCry(d);//旺旺旺
		
		Animal dd = new Dog();//自动向上转型
		animalCry(dd);
		
		Dog dd2 = (Dog)dd;//强制向下转型
		//dd.seeDoor();//报错
		dd2.seeDoor();//
		
		//Animal c = new Cat();
		//Dog dd3 = (Dog)c;//类型转化错误
		//dd3.seeDoor();//报错
	}
	
	static void animalCry(Animal a) {
		a.shout();
	}

}

class Animal{
	public void shout() {
		System.out.println("叫了一声!");
	}
}

class Dog extends Animal{
	public void shout() {
		System.out.println("旺旺旺");
	}
	
	public void seeDoor() {
		System.out.println("看门!!");
	}
}

class Cat extends Animal{
	public void shout() {
		System.out.println("喵喵喵!");
	}
}

5.7 final关键字

final关键字的作用:

      1. 修饰变量: 被他修饰的变量不可改变。一旦赋了初值,就不能被重新赋值。

1

final  int   MAX_SPEED = 120;

      2. 修饰方法:该方法不可被子类重写。但是可以被重载!

1

final  void  study(){}

      3. 修饰类: 修饰的类不能被继承。比如:Math、String等。

1

final   class  A {}

5.8 抽象方法和抽象类

 

抽象方法

      使用abstract修饰的方法,没有方法体,只有声明。定义的是一种“规范”,就是告诉子类必须要给抽象方法提供具体的实现。

·抽象类

      包含抽象方法的类就是抽象类。通过abstract方法定义规范,然后要求子类必须定义具体实现。通过抽象类,我们就可以做到严格限制子类的设计,使子类之间更加通用。

抽象类的使用要点:

     1. 有抽象方法的类只能定义成抽象类

     2. 抽象类不能实例化,即不能用new来实例化抽象类。

     3. 抽象类可以包含属性、方法、构造方法。但是构造方法不能用来new实例,只能用来被子类调用。

     4. 抽象类只能用来被继承。

     5. 抽象方法必须被子类实现

package cn.nls.oop;
/**
 * 抽象类的意义就在于:为子类提供统一的、规范的模板。子类必须实现相关的抽象方法
 * @author 
 *
 */
public abstract class Animal {
	//第一:没有实现。第二:子类必须实现
	abstract public void shout();
	
	public void run() {
		System.out.println("跑跑");
	} 
	
	public static void main(String[] args) {
		Animal a = new Dog();
	}

}

class Dog extends Animal{

	@Override
	public void shout() {
		System.out.println("汪汪汪");
		
	}

	
}

5.9.1 接口的作用

 接口就是比“抽象类”还“抽象”的“抽象类”,可以更加规范的对子类进行约束。全面地专业地实现了:规范具体实现的分离。

 抽象类还提供某些具体实现,接口不提供任何实现,接口中所有方法都是抽象方法。接口是完全面向规范的,规定了一批类具有的公共方法规范。

      从接口的实现者角度看,接口定义了可以向外部提供的服务

      从接口的调用者角度看,接口定义了实现者能提供那些服务

接口就是规范,定义的是一组规则,体现了现实世界中“如果你是…则必须能…”的思想

5.9.2 如何定义和使用接口

声明格式:

1

2

3

4

[访问修饰符]  interface 接口名   [extends  父接口1,父接口2…]  {

常量定义;  

方法定义;

}

定义接口的详细说明:

      1. 访问修饰符:只能是public或默认。

      2. 接口名:和类名采用相同命名机制。

      3. extends:接口可以多继承。  接口完全支持多继承。和类的继承类似,子接口扩展某个父接口,将会获得父接口中所定义的一切。

      4. 常量:接口中的属性只能是常量,总是:public static final 修饰。不写也是。

      5. 方法:接口中的方法只能是:public abstract。 省略的话,也是public abstract。

要点

      1. 子类通过implements来实现接口中的规范。

      2. 接口不能创建实例,但是可用于声明引用变量类型。

      3. 一个类实现了接口,必须实现接口中所有的方法,并且这些方法只能是public的

      4. JDK1.7之前,接口中只能包含静态常量、抽象方法,不能有普通属性、构造方法、普通方法。

      5. JDK1.8后,接口中包含普通的静态方法。

编程1:

package cn.nls.oop;

import java.awt.desktop.SystemSleepEvent;

/**
 * 测试接口和实现类
 * @author 
 *
 */
public class TestInterface {
	public static void main(String[] args) {
		Volant v = new Angel();
		v.fly();
		
		Honst h = new GoodMan();
		h.helpOther();
	}
}

/**
 * 飞行接口
 * @author 
 *
 */
interface Volant{
	int FLY_HEIGHT=1000;
	void fly();
}

interface Honst{
	void helpOther();
}

class Angel implements Volant,Honst{//实现类可以实现多个父接口

	@Override
	public void helpOther() {
		System.out.println("Angel.helpOther()");
		
	}

	@Override
	public void fly() {
		System.out.println("Angel.fly()");
		
	}
	
}


class GoodMan implements Honst{

	@Override
	public void helpOther() {
		System.out.println("GoodMan.helpOther()");
		
	}
	
}

class Birdman implements Volant{

	@Override
	public void fly() {
		System.out.println("Birdman.fly()");
		
	}
	
}

编程2:

package cn.nls.oop;

public class TestInterface2 {

}

interface A{
	void testa();
}
interface B{
	void testb();
}

/**接口可以多继承:接口C继承接口A和B;类单继承*/
interface C extends A,B{
	void testc();
}
class Mysubclass implements C{

	@Override
	public void testa() {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void testb() {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void testc() {
		// TODO Auto-generated method stub
		
	}
	
}

5.10.1 内部类的概念

内部类可以使用public、default、protected 、private以及static修饰。而外部顶级类(我们以前接触的类)只能使用public和default修饰。

内部类只是一个编译时概念,一旦我们编译成功,就会成为完全不同的两个类。对于一个名为Outer的外部类和其内部定义的名为Inner的内部类。编译完成后会出现Outer.class和Outer$Inner.class两个类的字节码文件。所以内部类是相对独立的一种存在,其成员变量/方法名可以和外部类的相同。

内部类的作用:

      1. 内部类提供了更好的封装。只能让外部类直接访问,不允许同一个包中的其他类直接访问。

      2. 内部类可以直接访问外部类的私有属性,内部类被当成其外部类的成员。 但外部类不能访问内部类的内部属性。

      3. 接口只是解决了多重继承的部分问题,而内部类使得多重继承的解决方案变得更加完整。

内部类的使用场合:

      1. 由于内部类提供了更好的封装特性,并且可以很方便的访问外部类的属性。所以,在只为外部类提供服务的情况下可以优先考虑使用内部类。

      2.  使用内部类间接实现多继承:每个内部类都能独立地继承一个类或者实现某些接口,所以无论外部类是否已经继承了某个类或者实现了某些接口,对于内部类没有任何影响。

5.10.2内部类的分类

在Java中内部类主要分为成员内部类(非静态内部类、静态内部类)、匿名内部类、局部内部类。

a) 非静态内部类(外部类里使用非静态内部类和平时使用其他类没什么不同)

      i. 非静态内部类必须寄存在一个外部类对象里。因此,如果有一个非静态内部类对象那么一定存在对应的外部类对象。非静态内部类对象单独属于外部类的某个对象。

      ii. 非静态内部类可以直接访问外部类的成员,但是外部类不能直接访问非静态内部类成员。

      iii. 非静态内部类不能有静态方法、静态属性和静态初始化块。

      iv. 外部类的静态方法、静态代码块不能访问非静态内部类,包括不能使用非静态内部类定义变量、创建实例。

      v. 成员变量访问要点:

        1. 内部类里方法的局部变量:变量名。

        2. 内部类属性:this.变量名。

        3. 外部类属性:外部类名.this.变量名。

package cn.nls.oop;
/**
 * 测试非静态内部类
 * @author admin
 *
 */

public class TestInnerClass {
	public static void main(String[] args) {
		
		//创建内部类对象
		Outer.Inner inner = new Outer().new Inner();
		inner.show();
	}

}

class Outer{
	private int age = 20;
	
	public void testOuter() {
		System.out.println("Outer.testOuter()");
	}
	
	class Inner{
		int age = 10;
		public void show() {
			int age = 30;
			System.out.println("外部类的成员变量age:"+Outer.this.age);
			System.out.println("内部类的成员变量age:"+this.age);
			System.out.println("局部变量age:"+age);
		}
	}
}

b) 静态内部类

i. 使用要点:

       1. 当一个静态内部类对象存在,并不一定存在对应的外部类对象。 因此,静态内部类的实例方法不能直接访问外部类的实例方法。

       2. 静态内部类看做外部类的一个静态成员。 因此,外部类的方法中可以通过:“静态内部类.名字”的方式访问静态内部类的静态成员,通过 new 静态内部类()访问静态内部类的实例。

package cn.nls.oop;
/**
 * 测试静态内部类
 * @author admin
 *
 */
public class TestStaticInnerClss {
	public static void main(String[] args) {
		
		Outer2.Inner2 inner = new Outer2.Inner2();
		
	}
}

class Outer2{
	
	static class Inner2{
		
	}
}

. 匿名内部类

适合那种只需要使用一次的类。比如:键盘监听操作等等。

package cn.nls.oop;
/**
 * 测试匿名内部类
 * @author admin
 *
 */
public class TestAnonymousInnerClaass {
	
	public static void test01(AA a) {
		System.out.println("###########");
		a.aa();
	}
	
	public static void main(String[] args) {
		TestAnonymousInnerClaass.test01(new AA() {
			
			@Override
			public void aa() {
				// TODO Auto-generated method stub
				System.out.println("TestAnonymousInnerClaass.main(...)new AA() {...}.aa()");
			}
		});
	}
	
}


interface AA{
	void aa();
}

注意

      1. 匿名内部类没有访问修饰符。

      2. 匿名内部类没有构造方法。因为它连名字都没有那又何来构造方法呢。

.局部内部类

      还有一种内部类,它是定义在方法内部的,作用域只限于本方法,称为局部内部类。

      局部内部类的的使用主要是用来解决比较复杂的问题,想创建一个类来辅助我们的解决方案,到那时又不希望这个类是公共可用的,所以就产生了局部内部类。局部内部类和成员内部类一样被编译,只是它的作用域发生了改变,它只能在该方法中被使用,出了该方法就会失效。

      局部内部类在实际开发中应用很少。

5.11.1 String基础

1. String类又称作不可变字符序列。

2. String位于java.lang包中,Java程序默认导入java.lang包下的所有类。

3. Java字符串就是Unicode字符序列,例如字符串“Java”就是4个Unicode字符’J’、’a’、’v’、’a’组成的。

4.  Java没有内置的字符串类型,而是在标准Java类库中提供了一个预定义的类String,每个用双引号括起来的字符串都是String类的一个实例。

5. Java允许使用符号"+"把两个字符串连接起来。

符号"+"把两个字符串按给定的顺序连接在一起,并且是完全按照给定的形式。

当"+"运算符两侧的操作数中只要有一个是字符串(String)类型,系统会自动将另一个操作数转换为字符串然后再进行连接。

package cn.nls.oop;
/**
 * 
 * @author admin
 *
 */
public class TestString {
	public static void main(String[] args) {
		String str = "abc";
		String str2 = new String("def");
		String str3 = "abc"+"defgh";
		String str4 = "abc"+66;//不是加法,是字符串连接符
		
		System.out.println(str4);
		
		System.out.println("################");
		String str10 = "giaogiao";
		String str11 = "giaogiao";
		String str12 = new String("giaogiao");
		
		System.out.println(str10==str11);
		System.out.println(str10==str12);//不是同一个对象
		
		//通常比较字符串时,使用equals
		System.out.println(str12.equals(str11));
		
		
	}
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值