【Java基础】第6章_面向对象(下)-2:抽象类与抽象方法、接口

PPT链接:点击这里

5、抽象类与抽象方法

随着继承层次中一个个新子类的定义,类变得越来越具体,而父类则更一般,更通用。类的设计应该保证父类和子类能够共享特征。有时将一个父类设计得非常抽象,以至于它没有具体的实例,这样的类叫做抽象类

5.1、abstract的使用

  • abstract的理解:抽象的
  • abstract可以用来修饰的结构:类、方法

abstract修饰类:抽象类

  • 此类不能实例化
  • 抽象类中一定有构造器,便于子类实例化时调用(涉及:子类对象实例化的全过程)
  • 开发中,都会提供抽象类的子类,让子类对象实例化,完成相关的操作

abstract修饰方法:抽象方法

  • 抽象方法只有方法的声明,没有方法体

  • 包含抽象方法的类,一定是一个抽象类。反之,抽象类中可以没有抽象方法

  • 若子类重写了父类中的所有的抽象方法后,此子类方可实例化

    若子类没有重写父类中的所有的抽象方法,则此子类也是一个抽象类,需要使用abstract修饰

注意点

  • abstract不能用来修饰:属性、构造器等结构
  • abstract不能用来修饰私有方法、静态方法、final的方法、final的类 ----- 因为这些方法都不能被重写
public class AbstractTest {
	public static void main(String[] args) {
		
		//一旦Person类抽象了,就不可实例化
//		Person p1 = new Person();
	}
}

abstract class Creature{
	public abstract void breath();
}

abstract class Person extends Creature{
	String name;
	int age;
	
	public Person(){//子类对象实例化会用到
	}
	public Person(String name,int age){
		this.name = name;
		this.age = age;
	}
	
	//抽象方法:只有方法的声明,没有方法体
	public abstract void eat();
	
	public void walk(){
		System.out.println("人走路");
	}
}

class Student extends Person{
	
	public Student(String name,int age){
		super(name,age);
	}
	public Student(){
	}
	
    //重写抽象方法
	public void eat(){
		System.out.println("学生多吃有营养的食物");
	}

	@Override//重写抽象方法
	public void breath() {
		System.out.println("学生应该呼吸新鲜的没有雾霾的空气");
	}
}

5.2、抽象类应用

抽象类是用来模型化那些父类无法确定全部实现,而是由其子类提供具体实现的对象的类

5.3、抽象类的匿名子类

  • 涉及到匿名的东西,都是只用一次,目的就是为了省事。有时候不一定用,但是为了看得懂代码,这个还是要有所了解的

  • 匿名分为以下 4 种情况:

    • 非匿名类的非匿名对象
    • 非匿名类的匿名对象
    • 匿名子类的非匿名对象
    • 匿名子类的匿名对象
public class PersonTest {
    public static void main(String[] args) {
        
        //********1.非匿名类的匿名对象********(对象没有名,类有名:Worker)
    	method(new Worker());    
        System.out.println("************************************");
        
        
        //********2.非匿名类的非匿名对象w********(对象有名:w,类有名:worker)
        Worker w = new Worker();
        method(w);               
        System.out.println("************************************");
        
        
        //********3.匿名子类的非匿名对象********
        //是new了一个对象,但不是new了一个抽象类的对象,是创建了一个匿名(这个类没有名)子类(Person的子类)对象,对象名为p
        //因为我们暂时还不知道子类的名字是什么,所以借助子类重写方法来实现方法的调用
        Person p = new Person() {
            @Override //这里说明了创建的类是Person的子类,注意重写相关的方法
            public void eat() {
                System.out.println("吃东西");
            }
            @Override
            public void breath() {
                System.out.println("好好呼吸");
            }
        };
        method(p);//会调用对象重写的方法,还涉及到了多态的使用
        System.out.println("************************************");
        
        
        //********4.匿名子类的匿名对象(连对象名也省去)********
        method(new Person(){ 
            @Override//这里也说明了创建的类是Person的子类,注意重写相关的方法
            public void eat() {
                System.out.println("吃好吃东西");
            }
            @Override
            public void breath() {
                System.out.println("好好呼吸新鲜空气");
            }
        });
        
    }

    public static void method(Person p){
        p.breath();
        p.eat();
    }
}


abstract class Person {//抽象类也可以继承,比如Person是可以继承别的类的
    public Person() {
    	
    }
    public abstract void eat();
    public abstract void breath();
}


class Worker extends Person{   
    @Override
    public void eat() {
    }

    @Override
    public void breath() {
    }
}

5.4、练习

5.4.1、练习1

编写一个 Employee 类,声明为抽象类,包含如下三个属性:name, id, salary
提供必要的构造器和抽象方法:work()

对于 Manager 类来说,他既是员工,还具有奖金(bonus)的属性
请使用继承的思想,设计 CommonEmployee 类和 Manager 类,要求类中提供必要的方法进行属性访问

【代码实现】

public abstract class Employee {
	private String name;
	private int id;
	private double salary;
	
	public Employee(){
		super();
	}

	public Employee(String name, int id, double salary) {
		super();
		this.name = name;
		this.id = id;
		this.salary = salary;
	}
	
	public abstract void work();	
}
public class Manager extends Employee{
	private double bonus;	//奖金
	
	public Manager(double bonus) {
		super();
		this.bonus = bonus;
	}
	public Manager(String name, int id, double salary, double bonus) {
		super(name, id, salary);
		this.bonus = bonus;
	}

	@Override
	public void work() {
		System.out.println("管理员工,提高公司运行效率");		
	}
}
public class CommonEmployee extends Employee {

	@Override
	public void work() {
		System.out.println("员工在一线车间生产产品");
	}
}
public class EmployeeTest {
	public static void main(String[] args) {
		
        //多态
		Employee manager = new Manager("库克",1001,5000,50000);
		manager.work();
		
		CommonEmployee commonEmployee = new CommonEmployee();
		commonEmployee.work();
	}
}

5.4.2、练习2

编写工资系统,实现不同类型员工(多态)的按月发放工资。如果当月出现某个Employee对象的生日,则将该雇员的工资增加100元

实验说明:
1.定义一个Employee类,该类包含:
	private成员变量name,number,birthday,其中birthday为MyDate类的对象
	abstract方法earnings()
	toString()方法输出对象的name,number和birthday

2.MyDate类包含:
	private成员变量year,month,day
	toDateString()方法返回日期对应的字符串:xxxx年xx月xx日

3.定义SalariedEmployee类继承Employee类,实现按月计算工资的员工处理。该类包括:
	private成员变量monthlySalary
实现父类的抽象方法earnings(),该方法返回monthlySalary值;toString()方法输出员工类型信息及员工的name,number,birthday

4.参照SalariedEmployee类定义HourlyEmployee类,实现按小时计算工资的员工处理。该类包括:
	private成员变量wage和hour
实现父类的抽象方法earnings(),该方法返回wage * hour值
toString()方法输出员工类型信息及员工的name,number,birthday

5.定义PayrollSystem类,创建Employee变量数组并初始化,该数组存放各类雇员对象的引用
利用循环结构遍历数组元素,输出各个对象的类型,name,number,birthday
当键盘输入本月月份值时,如果本月是某个Employee对象的生日,还要输出增加工资信息

提示:
定义People类型的数组People c1[]=new People[10];
数组元素赋值:
	c1[0]=new People("John","0001",20);
	c1[1]=new People("Bob","0002",19);
若People有两个子类Student和Officer,则数组元素赋值时,可以使父类类型的数组元素指向子类
	c1[0]=new Student("John","0001",20,85.0);
	c1[1]=new Officer("Bob","0002",19,90.5);

【代码实现】

public abstract class Employee {
	private String name;
	private int number;
	private MyDate birthday;
	
	public Employee(String name, int number, MyDate birthday) {
		super();
		this.name = name;
		this.number = number;
		this.birthday = birthday;
	}

	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}

	public int getNumber() {
		return number;
	}
	public void setNumber(int number) {
		this.number = number;
	}

	public MyDate getBirthday() {
		return birthday;
	}
	public void setBirthday(MyDate birthday) {
		this.birthday = birthday;
	}

	public abstract double earnings();//抽象方法

	@Override
	public String toString() {
		return "name=" + name + ", number=" + number + ", birthday=" + birthday.toDateString();
        //不写toDateString(),则birthday会默认调用toString(),是地址值
	}
	
}
public class MyDate {
	private int year;
	private int month;
	private int day;
	
	public MyDate(int year, int month, int day) {
		super();
		this.year = year;
		this.month = month;
		this.day = day;
	}

	public int getYear() {
		return year;
	}
	public void setYear(int year) {
		this.year = year;
	}

	public int getMonth() {
		return month;
	}
	public void setMonth(int month) {
		this.month = month;
	}

	public int getDay() {
		return day;
	}
	public void setDay(int day) {
		this.day = day;
	}

	public String toDateString(){
		return year + "年" + month + "月" + day + "日";
	}
}
public class SalariedEmployee extends Employee{
	private double monthlySalary;//月工资

	public SalariedEmployee(String name,int number,MyDate birthday) {
		super(name,number,birthday);
	}
	public SalariedEmployee(String name, int number, MyDate birthday, double monthlySalary) {
		super(name, number, birthday);
		this.monthlySalary = monthlySalary;
	}

	@Override
	public double earnings() {//重写earnings()抽象方法
		return monthlySalary;		
	}

	@Override
	public String toString() {
		return "SalariedEmployee [" + super.toString() + "]";
	}	
}
public class HourlyEmployee extends Employee{
	private int wage;//每小时的工资
	private int hour;//月工作的小时数

	public HourlyEmployee(String name, int number, MyDate birthday) {
		super(name, number, birthday);
	}
	public HourlyEmployee(String name, int number, MyDate birthday,int wage,int hour) {
		super(name, number, birthday);
		this.wage = wage;
		this.hour = hour;
	}

	public int getWage() {
		return wage;
	}
	public void setWage(int wage) {
		this.wage = wage;
	}
    
	public int getHour() {
		return hour;
	}
	public void setHour(int hour) {
		this.hour = hour;
	}
    
	@Override
	public double earnings() {
		return wage * hour;
	}
	
	public String toString(){
		return "HourlyEmployee[" + super.toString() + "]"; 
	}
}
import java.util.Scanner;

public class PayrollSystem {
	public static void main(String[] args) {
		Scanner scanner = new Scanner(System.in);
		System.out.println("请输入当月的月份:");
		int month = scanner.nextInt();
		
		Employee[] emps = new Employee[2];
		
		emps[0] = new SalariedEmployee("马森", 1002,new MyDate(1992, 2, 28),10000);
		emps[1] = new HourlyEmployee("潘雨生", 2001, new MyDate(1991, 1, 6),60,240);
		
		for(int i = 0;i < emps.length;i++){
			System.out.println(emps[i]);
			double salary = emps[i].earnings();
			System.out.println("月工资为:" + salary);
			
			if(month == emps[i].getBirthday().getMonth()){
				System.out.println("生日快乐!奖励 100 元");
			}
		}
	}
}

6、接口(interface)

  • 一方面,有时必须从几个类中派生出一个子类,继承它们所有的属性和方法。但是Java 不支持多重继承。有了接口,就可以得到多重继承的效果
  • 另一方面,有时必须从几个类中抽取出一些共同的行为特征,而它们之间又没有 is-a 的关系,仅仅是具有相同的行为特征而已。例如:鼠标、键盘、打印机、扫描仪、摄像头、充电器、MP3 机、手机、数码相机、移动硬盘等都支持 USB 连接
  • 接口就是规范,定义的是一组规则,体现了现实世界中 “如果你是/要…则必须能…” 的思想。继承是一个"是不是"的关系,而接口实现则是"能不能"的关系
  • 接口的本质是契约,标准,规范,就像我们的法律一样。制定好后大家都要遵守

6.1、接口的使用

  • 接口使用interface来定义

  • Java 中,接口和类是并列的两个结构

  • 如何定义两个接口:定义接口中的成员

    • JDK7 及以前:只能定义全局常量抽象方法

      全局常量:public static final 的,书写时可以省略不写

      抽象方法:public abstract 的,书写时可以省略不写

    • JDK8:除了全局常量和抽象方法之外,还可以定义静态方法、默认方法

  • 接口中不能定义构造器,意味着接口不可以实例化

  • Java 开发中,接口通过让类去实现(implements)的方式来使用

    如果实现类覆盖了接口中的所有方法,则此实现类就可以实例化

    如果实现类没有覆盖接口中所有的抽象方法,则此实现类仍为一个抽象类

  • Java类可以实现多个接口,弥补了Java 单继承性的局限性。格式:class AA extends BB implementd CC,DD,EE

  • 接口与接口之间可以继承,而且可以多继承

  • 接口的具体使用,体现多态性

    接口的主要用途就是被实现类实现(面向接口编程)

  • 接口,实际可以看作是一种规范

public class InterfaceTest {
	public static void main(String[] args) {
		System.out.println(Flyable.MAX_SPEED);
		System.out.println(Flyable.MIN_SPEED);
//		Flyable.MIN_SPEED = 2;//编译错误,不能赋值
		
		Plane plane = new Plane();
		plane.fly();
	}
}

interface Flyable{
	//全局常量
	public static final int MAX_SPEED = 7900;//第一宇宙速度
	int MIN_SPEED = 1;//省略了public static final
	
	//抽象方法
	public abstract void fly();
	void stop();//省略了public abstract
	
	//接口中不能定义构造器
//	public Flyable(){
//		
//	}
}


interface Attackable{
	void attack();
}


class Plane implements Flyable{

	@Override
	public void fly() {
		System.out.println("通过引擎起飞");
	}

	@Override
	public void stop() {
		System.out.println("驾驶员减速停止");
	}
}


abstract class Kite implements Flyable{//实现类没有覆盖接口中所有的抽象方法,则此实现类仍为一个抽象类
    
	@Override
	public void fly() {
	}
}


class Bullet extends Object implements Flyable,Attackable,CC{

	@Override
	public void attack() {
	}

	@Override
	public void fly() {	
	}
	@Override
	public void stop() {
	}

	@Override
	public void method1() {
	}
	@Override
	public void method2() {
	}
}

//************************************
interface AA{
	void method1();
}
interface BB{
	void method2();
}

interface CC extends AA,BB{//继承
    
}

6.2、接口的应用举例

  1. 接口使用上也满足多态性

  2. 接口,实际上就是定义了一种规范

  3. 开发中,体会面向接口编程

【举例1】

【举例2】

【举例3】

public class USBTest {
	public static void main(String[] args) {
		
		Computer com = new Computer();
		//1.创建了接口的 非匿名实现类的非匿名对象
		Flash flash = new Flash();
		com.transferData(flash);
		
		//2.创建了接口的 非匿名实现类的匿名对象
		com.transferData(new Printer());
		
		//3.创建了接口的 匿名实现类的非匿名对象
		USB phone = new USB(){
			@Override
			public void start() {
				System.out.println("手机开始工作");
			}
			@Override
			public void stop() {
				System.out.println("手机结束工作");
			}
		};
		com.transferData(phone);
		
		//4.创建了接口的 匿名实现类的匿名对象
		com.transferData(new USB(){
			@Override
			public void start() {
				System.out.println("mp3开始工作");
			}
			@Override
			public void stop() {
				System.out.println("mp3结束工作");
			}
		});
	}
}

class Computer{
	//电脑传输数据
	public void transferData(USB usb){//USB usb = new Flash();
		usb.start();
		System.out.println("具体传输数据的细节");
		usb.stop();
	}
}

interface USB{
	//常量:定义了长、宽、最大最小的传输速度等
	void start();
	void stop();
}

class Flash implements USB{
	@Override
	public void start() {
		System.out.println("U盘开启工作");
	}
	@Override
	public void stop() {
		System.out.println("U盘结束工作");
	}
}

class Printer implements USB{
	@Override
	public void start() {
		System.out.println("打印机开启工作");
	}
	@Override
	public void stop() {
		System.out.println("打印机结束工作");
	}
}

6.3、抽象类和接口之间的对比

面试题:抽象类与接口有哪些异同


在开发中,常看到一个类不是去继承一个已经实现好的类,而是要么继承抽象类,要么实现接口

6.4、练习

6.4.1、练习1

判断下列哪条有错误,如有错误应该怎么改?

interface A {
	int x = 0;
}
class B {
	int x = 1;
}

class C extends B implements A {
	public void pX() {
		System.out.println(x);//编译不通过,x不明确
        //改
//		System.out.println(super.x); //1 ——调用父类B
//		System.out.println(A.x);//0 ——调用接口A
	}
    
	public static void main(String[] args) {
		new C().pX();
	}
}

6.4.2、练习2

判断下列哪条有错误,如有错误应该怎么改?

interface Playable {
	void play();
}
interface Bounceable {
	void play();
}

interface Rollable extends Playable, Bounceable {
	Ball ball = new Ball("PingPang"); 
    //ball是全局常量,这里省略了public static final,应该是public static final Ball ball = new Ball("PingPang");
}

public class Ball implements Rollable {
	private String name;
	public String getName() {
		return name;
	}
	public Ball(String name) {
		this.name= name;
	}
    
	public void play() {//可以看做上面两个接口中的play()都被重写了
		ball = new Ball("Football"); //ball不能重新赋值,ball是全局常量
		System.out.println(ball.getName());
	}
}

6.4.3、练习3

1.定义一个接口用来实现两个对象的比较
interface CompareObject{
	public int compareTo(Object o);//若返回值是0,代表相等;若为正数,代表当前对象大;负数代表当前对象小
}
2.定义一个Circle类,声明radius属性,提供getter和setter方法
3.定义一个ComparableCircle类,继承Circle类并且实现CompareObject接口。在ComparableCircle类中给出接口中方法compareTo的实现体,用来比较两个圆的半径大小
4.定义一个测试类InterfaceTest,创建两个ComparableCircle对象,调用compareTo方法比较两个类的半径大小
5.思考:参照上述做法定义矩形类Rectangle和ComparableRectangle类,在ComparableRectangle类中给出compareTo方法的实现,比较两个矩形的面积大小

【代码实现】

public interface CompareObject {
	public int compareTo(Object o); //若返回值是0 , 代表相等; 若为正数,代表当前对象大;负数代表当前对象小
}
public class Circle {
	private double radius;

	public double getRadius() {
		return radius;
	}
	public void setRadius(double radius) {
		this.radius = radius;
	}

	public Circle() {
		super();
	}
	public Circle(double radius) {
		super();
		this.radius = radius;
	}
}
public class ComparableCircle extends Circle implements CompareObject{
	
	public ComparableCircle(double radius) {//也可以用ComparableCircle造圆
		super(radius);
	}
    
	@Override
	public int compareTo(Object o) {
		if(this == o){//是同一个引用
			return 0;
		}
		if(o instanceof ComparableCircle){
			ComparableCircle c = (ComparableCircle)o;
			//错误的:强转会丢失精度
//			return (int) (this.getRadius() - c.getRadius());
			//正确的:
			if(this.getRadius() > c.getRadius()){
				return 1;
			}else if(this.getRadius() < c.getRadius()){
				return -1;
			}else{
				return 0;
			}
			//当属性radius声明为Double类型时,可以调用包装类的方法
//			return this.getRadius().compareTo(c.getRadius());//Double类实现Comparable接口,Comparable接口中有compareTo()
		}else{
			throw new RuntimeException("传入的数据类型不匹配");
		}
	}
}
public class ComparableCircleTest {
	public static void main(String[] args) {
		
		ComparableCircle c1 = new ComparableCircle(3.4);
		ComparableCircle c2 = new ComparableCircle(3.6);
		
		int compareValue = c1.compareTo(c2);
		if(compareValue > 0){
			System.out.println("c1对象大");
		}else if(compareValue < 0){
			System.out.println("c2对象大");
		}else{
			System.out.println("c1与c2一样大");
		}	
        
		int compareValue1 = c1.compareTo(new String("AA"));//若两个对象的类型不一样
		System.out.println(compareValue1);
	}
}

6.5、Java 8 中关于接口的改进

Java8 中,可以为接口添加静态方法和默认方法。从技术角度来说,这是完全合法的,只是它看起来违反了接口作为一个抽象定义的理念

  • 静态方法:使用 static 关键字修饰。可以通过接口直接调用静态方法,并执行其方法体。我们经常在相互一起使用的类中使用静态方法。你可以在标准库中找到像 Collection/Collections 或者 Path/Paths 这样成对的接口和类
  • 默认方法:默认方法使用 default 关键字修饰。可以通过实现类对象来调用。我们在已有的接口中提供新方法的同时,还保持了与旧版本代码的兼容性。比如:java 8 API 中对 Collection、List、Comparator 等接口提供了丰富的默认方法
public interface CompareA {
	//静态方法
	public static void method1() {
		System.out.println("CompareA:北京");
	}
	
	//默认方法
	public default void method2(){
		System.out.println("CompareA:上海");
	}
	//public可以省略
	default void method3(){
		System.out.println("CompareA:上海");
	}
}
public interface CompareB {
	default void method3(){
		System.out.println("CompareB:上海");
	}
}
public class SuperClass {
	public void method3(){
		System.out.println("SuperClass:北京");
	}
}
public class SubClassTest {
	public static void main(String[] args) {
		SubClass s = new SubClass();
//		s.method1();//错误
//		SubClass.method1();//错误
//		知识点1:接口中定义的静态方法,只能通过接口来调用,不能通过实现类或实现类的对象调用
        CompareA.method1();
        
        
//		知识点2:通过实现类的对象,可以调用接口中的默认方法。如果实现类重写了接口中的默认方法,调用时,仍然调用的是重写以后的方法
		s.method2();//SubClass:上海
        
        
//		知识点3:如果子类(或实现类)继承的父类和实现的接口中声明了同名同参数的默认方法,
//		那么子类在没有重写此方法的情况下,默认调用的是父类中的同名同参数的方法。-->类优先原则
        
        
//		知识点4:如果实现类实现了多个接口(此时没有继承父类),而这多个接口中定义了同名同参数的默认方法,
//      那么在实现类没有重写此方法的情况下,报错。-->接口冲突
//		这就需要我们必须在实现类中重写此方法
		s.method3();//SubClass:深圳
	
	}
}


class SubClass extends SuperClass implements CompareA,CompareB{
	//重写
	public void method2(){//在类中重写接口的默认方法,default要去掉
		System.out.println("SubClass:上海");
	}
	
	public void method3(){
		System.out.println("SubClass:深圳");
	}
	
//	知识点 5:如何在子类(或实现类)的方法中调用父类、接口中被重写的方法
	public void myMethod(){
		method3(); //调用自己定义的重写的方法
		super.method3(); //调用的是父类中声明的
        
//		调用接口中的默认方法
		CompareA.super.method3();
		CompareB.super.method3();
	}
}

6.6、默认方法接口冲突问题

  • 若一个接口中定义了一个默认方法,而另外一个接口中也定义了一个同名同参数的方法(不管此方法是否是默认方法),在实现类同时实现了这两个接口时,会出现:接口冲突

    • 解决1:调用其中一个接口中的方法 ---- 接口名.super.方法名();

    • 解决2:在实现类中覆盖接口中的同名同参数的方法,用实现类的对象调用

  • 若一个接口中定义了一个默认方法,而父类中也定义了一个同名同参数的非抽象方法,则不会出现冲突问题。因为此时遵守:类优先原则,接口中具有相同名称和参数的默认方法会被忽略

    • 解决1:默认调用父类的方法,类优先原则

    • 解决2:重写该方法,用对象调用方法

    • 解决3:调用接口中的方法 ---- 接口名.super.方法名();

interface Filial {// 孝顺的
	default void help() {
		System.out.println("老妈,我来救你了");
	}
}

interface Spoony {// 痴情的
	default void help() {
		System.out.println("媳妇,我来救你了");
	}
}

class Father{
	public void help(){
		System.out.println("儿子,就我媳妇!");
	}
}


//********************发生接口冲突********************
class Man implements Filial, Spoony {
	public static void main(String[] args) {
		Man man = new Man();
		man.impHelp();//解决1:调用其中一个接口中的方法
		man.help();   //解决2:实现类覆盖接口中同名同参数的方法,用实现类的对象调用
	}
	

	@Override
	public void help() {//覆盖
		System.out.println("我该就谁呢?");
	}
	
	public void impHelp() {
		Filial.super.help();
		Spoony.super.help();
	}
}


//********************类优先原则********************
class Man extends Father implements Filial, Spoony {
	public static void main(String[] args) {
		Man man = new Man();
		man.help();   //解决1:重写前默认调用父类的方法,类优先原则
				      //解决2:重写后,调用重写后的方法
		man.impHelp();//解决3:调用接口中的方法
	}
	
	@Override
	public void help() {//覆盖重写
		System.out.println("我该就谁呢?");
	}
	
	public void impHelp() {
		Filial.super.help();
		Spoony.super.help();
	}
}

物竞天择,适者生存,加油吧!!!

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值