Java面向对象 (五) 接口、多态、内存管理

第八天:
1、接口:
  1)是一种数据类型(引用类型)
  2)由interface定义 -------------类是class ,接口与类是并列关系
  3)只能包含 常量 和 抽象方法 -------------不能写变量和普通方法

  4)接口不能被实例化(不能new对象) -----------不完整(因为接口里面都是抽象方法)
             抽象类也不能被实例化(不能new对象)
  5)接口是需要被实现的,实现类: implements 实现
    实现类必须重写接口中的所有抽象方法(重写时必须加public关键字)
  6)一个类可以实现多个接口,用逗号分隔
    若又继承又实现时,应先继承后实现(先亲爹后干爹)

        继承: 超类是亲爹,只能有一个
             一个超类可以有多个派生类(子类),
             一个派生类只能有一个超类(父类)
        接口: 接口是干爹,可以有多个

  7)接口可以继承接口(可以继承多个接口)

 接口里的东西就是让别人用的,所以默认访问权限一定是public,不能是 private、protected

同类型之间是 继承,不同类型之间是 实现;
    类和类----------------继承
    接口和接口------------继承
    类和接口---------------实现

举例:
2)的举例	 
	声明了一个接口:  interface Inter{ 
				   }
	声明了一个类:   class Aoo{
				   }
				 
3)的举例	
	//演示接口的语法
	interface Inter{
		public static final int NUM = 5;  //声明一个常量赋值为5
		public abstract void show(); //抽象方法
		int COUNT = 5;   //常量,前面不写,也默认有 public static final
		void test();    //前面不写,也默认有 public abstract

		int number; //编译错误,常量必须声明同时初始化(建议:常量名所有字母都大写)
					//这是在接口里,它默认就是常量,前面不写 也默认有public static final
					//若在类中,它才是变量
		void say(){} //编译错误,抽象方法不能有方法体
					  //这是在接口中,Java默认它就是抽象方法
	}

4)的举例
	//接口的演示
	public class InterfaceDemo {
	    public static void main(String[] args) {
	   	 	  Inter1 o1;  //正确,接口是数据类型,可以声明引用
	        //Inter5 o1 = new Inter5(); //编译错误,接口不能被实例化
	    }
	}	 

5)的举例	
	//演示接口的实现
	interface Inter1{
	    void show(); //这两方法是抽象方法;接口里面的默认访问权限是public
	    void test();
	}
	class Boo implements Inter1{
	    public void show(){} //重写接口中的抽象方法时,访问权限必须是public
	    public void test(){}
	    
	     //超类接口 已经是public 公开了(因为接口里默认访问权限是public),
	     //派生类什么都不写 就是小于超类方法的,要大于或等于,
         //所以实现类重写接口中的抽象方法时必须写 public

	//如果 是抽象类的子类 重写抽象方法,就不是必须写成public了,只要大于或等于就行
	}

6)的举例
	//演示接口的多实现
①	interface Inter2{
	    void show();
	}
	interface Inter3{
	    void test();
	}
	class Doo implements Inter2,Inter3{
	    public void show(){}
	    public void test(){}
	}
	
②	interface Inter2{
	    void show();
	}
	interface Inter3{
	    void test();
	}
	abstract class Coo{
	    abstract void say(); //这的abstract不能省,只有接口里才默认,普通类不能省
	}
	class Doo extends Coo implements Inter2,Inter3{
	    public void show(){}
	    public void test(){}
	    void say(){} //这里可不写public,Doo、Coo两个都是类,都是默认的(大于或等于)
	}

7)的举例	
	//演示接口的继承
	interface Inter4{
	    void show();
	}
	interface Inter5 extends Inter4{ //现在Inter5有两个抽象方法(继承了Inter4一个)
	    void test();
	}
	class Eoo implements Inter5{
	    public void test(){}
	    public void show(){}
	}

注:	
①	//一个接口可以继承多个接口
	interface Inter1{
		void show();
	}
	interface Inter2{
		void test();
	}
	interface Inter3 extends Inter1,Inter2{
		void say();    //一个接口可以继承多个接口
	}
②	
	interface Inter1{
	void show();
	}
	interface Inter2 extends Inter1{
	void test();
	}
	class Aoo implements Inter2{  //Inter超类(接口是干爹)  Aoo派生类(子类)
	public void show(){}
	public void test(){}
	}
	//Inter2 o1 = new Inter2();  //编译错误,接口不能被实例化
	Inter2 o2 = new Aoo();   //正确,向上造型(接口是干爹,也是父类)
	Inter1 o3 = new Aoo();   //正确,向上造型,继承有传递性
					//Inter1爷爷辈,Inter2爸爸辈,Aoo孙子辈(Inter1也属于Aoo的父类)
					
		// 向上造型:超类型的引用指向派生类的对象

设计规则:
 1)将所有派生类所共有的属性和行为,抽到超类中-------------抽共性
 2)若派生类的行为都一样,设计为 普通方法
  若派生类的行为都不一样,设计为 抽象方法

 3)将部分派生类 所共有的属性和行为,抽到接口中
  接口是对继承单根性的扩展----------------------实现多继承

举例:
子弹与敌人的碰撞,若撞上了:
 1)子弹直接消失,敌人先爆破再消失
 2)若被击中的是 小敌机,则玩家得1分
  若被击中的是 大敌机,则玩家得3分
      (得分 这个行为是小敌机 大敌机部分派生类共有的,所以 抽到接口中)

int getScore(); 如果 放到超类中,那子类都可以继承了,所以不行
class EnemyScore{ //若 单独放到一个类中,底下子类 一个类只能有一个超类(父类)
 int getScore();
}
          所以 抽到接口中(Java可以又继承类,又实现接口)

	class FlyingObject{ //超类
	  width,height,x,y
	  void step(){}
	  BufferedImage getImage(){}
	}
	interface EnemyScore{ //得分接口
	  int getScore();
	}
	
	class Airplane extends FlyingObject implements EnemyScore{
	  public int getScore(){ return 1; }  //重写抽象方法
	}
	class BigAirplane extends FlyingObject implements EnemyScore{
	  public int getScore(){ return 3; }
	}
	
	class Bullet extends FlyingObject{
	}
	class Hero extends FlyingObject{
	}
	class Sky extends FlyingObject{
	}

 若被击中的是小蜜蜂,则英雄机得奖励(1条命或40火力值)
 若被击中的是大黄蜂,则英雄机得奖励(2条命或100火力值)、玩家得8分

class FlyingObject{ //超类
  width,height,x,y
  void step(){}
  BufferedImage getImage(){}
}
interface EnemyScore{ //得分接口
  int getScore();
}
interface EnemyAward{ //奖励接口
  int getAwardType();
}
class Bee extends FlyingObject implements EnemyAward{
  public int getAwardType(){ ... }
}
class BigYellowBee extends FlyingObject implements EnemyAward,EnemyScore{
  public int getAwardType(){ ... }
  public int getScore(){ return 8; }
}

如何调错:---------------慢慢来,慢慢积累经验
 1、先锁定错误位置:
  ----将功能一个一个注释掉,再一个一个的放开,看哪个方法放开之后出的错误
 2、打桩:
  ----在你觉得有问题的位置,输出数据的值查看 System.out.println(数据);

第九天:
1、多态:
  1)多态的意义:       (因为 对象的行为不一样所以才抽象,表现出多态的效果了)
   1.1)同一类型的引用在指向不同的对象时,有不同的实现------所有抽象方法都是多态的
      ----行为的多态:cut()、step()、getImage()…
   1.2)同一个对象被造型为不同的类型时,有不同的功能------所有对象都是多态的(明上午细讲)
      ----对象的多态:我、水…

  2)向上造型(相当于 自动类型转换):
   2.1)超类型的引用指向派生类的对象
   2.2)能造型成为的数据类型有: 超类 或 它所实现的接口(干爹)
   2.3)能点出来什么,看引用的类型

  3)强制类型转换,成功的条件只有如下两种:
   3.1)引用所指向的对象,就是该类型
   3.2)引用所指向的对象,继承了该类或实现了该接口

  强转时若不符合如上两个条件,则发生 ClassCastException 类型转换异常

 建议:在强转之前应先通过instanceof来判断引用指向的对象是否是该类型

举例:

1.1)的举例:
	abstract class 人{ 
		abstract void cut(); 
	} 
	class 理发师 extends 人{ 
		void cut(){ 剪发 } 
	} 
	class 外科医生 extends 人{ 
		void cut(){ 开刀 } 
	} 
	class 演员 extends 人{ 
		void cut(){ 停止表演 }
	}
	
	人 p1 = new 理发师(); //向上造型
	人 p2 = new 外科医生(); 
	人 p3 = new 演员(); 
	p1.cut(); //剪发 
	p2.cut(); //开刀 
	p3.cut(); //停止表演

1.2)的举例:
	interface 讲师{ 
		void 授课(); 
	} 
	interface 孩子他妈{ 
		void 揍他(); 
	} 
	interface 老公的老婆{ 
		void 咬他(); 
		void 收工资(); 
	} 
	class 我 implements 讲师,孩子他妈,老公的老婆{ 
		public void 授课(){}  //重写 抽象方法
		public void 揍他(){} 
		public void 咬他(){} 
		public void 收工资(){} 
	}

	我 me = new 我(); 
	讲师 o1 = me;  //向上造型,接口也是超类(干爹也是爹) 
	孩子他妈 o2 = me; 
	老公的老婆 o3 = me;
	o1.授课(); 
	o2.揍他(); 
	o3.咬他(); 
	o3.收工资();

				//向上造型:超类型的引用指向派生类的对象

2)的举例:
  超类(大)    派生类(小)
FlyingObject o = new Airplane(); //向上造型了

 基本类型那叫 自动类型转换(把一个小类型赋值给一个大类型  从后往前),
 引用类型这叫 向上造型

FlyingObject类型能点出来的东西少
Airplane类型能点出来的东西多   所以 造型后 能点出来的东西少了

3)的举例:
如果还想 点出来的东西多,再强制类型转换回去
把小敌机造型成超类型了,能点出来的东西就少了,如果还想 点小敌机里的东西,那就把 大 再强制为 小

基本类型强转 一定能成功,不会报错,只会丢精度、溢出等;
引用类型强转 是有可能报错的;

package oo.day09;
//多态的演示
public class MultiTypeDemo {
    public static void main(String[] args) {
        Aoo o = new Boo(); //向上造型
        
  ①    Boo o1 = o; //报错,发生了语法错误,o是Aoo类型,o1是Boo类型,Aoo大,
        			//把大类型赋给小类型,所以报错;
        			//这里编译期语法都错误了,还没到运行期 创建对象 JVM分配内存
        					
        Boo o1 = (Boo)o; //引用o所指向的对象就是Boo类型(上面向上造型那句),符合条件一
        
  ②    Inter o2 = o; //编译错误,o和Inter没有关系,
	    			  //o是Aoo类型,一个干爹(接口),一个亲爹(父类)
        Inter o2 = (Inter)o; //引用o所指向的对象实现了Inter接口
        				 //o所指向的对象是Boo类型,Boo实现了Inter接口,符合条件二
        				 
        				   //条件一 二都不符合,Boo Coo是两个独立的子类
  ③     //Coo o3 = (Coo)o; //运行时会发生ClassCastException类型转换异常
  
  			if(o instanceof Boo){ //true
 			if(o instanceof Inter){ //true
 			
        if(o instanceof Coo){ //false(满足上面两条件是true,否则是false)
            Coo o3 = (Coo)o;
        }else{
            System.out.println("o不是Coo类型");
        }
			//以后做强转前,一定要先写这个if判断语句,再强转
    }
}

interface Inter{ }
class Aoo{ }
class Boo extends Aoo implements Inter{ }
class Coo extends Aoo{ }
  1. 接口的好处?
     ----判断时用接口,可以代表所有实现该接口的类-------复用性好
  2. 行为的多态:
     ----getScore()、getAwardType()都是多态的(不同对象有不同的实现)
    对象的多态:
     ----被撞对象f就是多态的(同一对象造型为不同类型时有不同的功能)
  3. 向上造型:
     ----将所有敌人装到FlyingObject数组中------代码复用
  4. 强制类型转换的原因?
     ----isLive()、isHit()、goDead()在FlyingObject中,那就不必强转
     ----我们想调用的getScore()、getAwardType()在超类中没有,那就得强转
  5. 为何要instanceof判断?
     ----避免类型转换异常

面试题:
1)重写与重载的区别?
 重写(override):发生在父子类中,方法名相同,参数列表相同
 重载(overload):发生在同一类中,方法名相同,参数列表不同

2)switch分支语句可以作用在哪些数据类型上?
  byte,short,int,char,枚举,String(JDK1.7才开始支持)

3)简述java的8种基本数据类型?
  —共分四大类: 整型,浮点型,布尔型,字符型
 3.1)byte:整型(字节型),1个字节
 3.2)short:整型(短整型),2个字节
 3.3)int:整型,4个字节
 3.4)long:整型(长整型),8个字节
 3.5)float:浮点型(单精度),4个字节
 3.6)double:浮点型(双精度),8个字节
 3.7)boolean:布尔型,1个字节,存储true或false
 3.8)char:字符型,2个字节,存储单个字符

4)简述public、private、protected以及默认权限的访问范围?
 ----都是访问控制修饰符,目的是为了保护数据的安全
 4.1)public:公开的,任何类
 4.2)private:私有的,本类
 4.3)protected:受保护的,本类、派生类、同包类
 4.4)默认的:什么也不写,本类、同包类

5)问:抽象类与接口的区别?
答:
①抽象类中可以包含变量、常量、普通方法和抽象方法,而接口中只能包含常量和抽象方法
②一个类只能继承一个抽象类,但是一个类可以实现多个接口
③实现类在实现接口后,必须重写接口中的所有抽象方法
  派生类继承抽象类后,可以也声明为抽象类,而并非必须重写所有抽象方法

或者:

相同点:
 抽象类和接口都是数据类型,都属于引用数据类型,有很多相同点,
 比如说都可以包含抽象方法,都不能被实例化,都需要被继承/实现才有意义
不同点:
1)抽象类中可以包含很多东西(常量、变量、普通方法、抽象方法、构造方法)
 而接口中只能包含常量和抽象方法
2)一个类只能继承一个抽象类,但可以实现多个接口,用逗号分隔
 继承抽象类的派生类,可以重写抽象方法也可以不重写,不重写将派生类也做成抽象类
  接口的实现类必须重写所有抽象方法
3)抽象类中成员的默认访问权限就是默认的,即同包中访问
 接口中成员的默认访问权限是public


//-------------------复用性差、扩展性差、维护性差-------垃圾代码
  if(f instanceof Airplane){ //--------------只能适用于Airplane类
    Airplane a = (Airplane)f;
    score += a.getScore();
  }
  if(f instanceof BigAirplane){ //-----------只能适用于BigAirplane类
     BigAirplane ba = (BigAirplane)f;
score += ba.getScore();
  }
  if(f instanceof Bee){ //-------------------只能适用于Bee类
    Bee bee = (Bee)f;
    int type = bee.getAwardType();
    switch(type){
  case 0:
    hero.addFire();
    break;
  case 1:
    hero.addLife();
    break;
}
  }
  if(f instanceof BigYellowBee){ //-----------只能适用于BigYellowBee类
    BigYellowBee byb = (BigYellowBee)f;
    score += byb.getScore();
int type = byb.getAwardType();
    switch(type){
  case 0:
    hero.addFire();
    break;
  case 1:
    hero.addLife();
    break;
}
  }
}}}

//--------------------复用性好、扩展性好、维护性好-----高质量代码
if(f instanceof EnemyScore){ //-------能适用于所有实现EnemyScore接口的类
    EnemyScore es = (EnemyScore)f;
    score += es.getScore();
  }
if(f instanceof EnemyAward){ //-------能适用于所有实现EnemyAward接口的类
    EnemyAward ea = (EnemyAward)f;
    int type = ea.getAwardType();
    switch(type){
  case 0:
    hero.addFire();
    break;
  case 1:
    hero.addLife();
    break;
}
  }

 ------------------------------------------------------
  //假设被撞对象为小敌机----------调用的是小敌机的getScore()----1分
  //假设被撞对象为大敌机----------调用的是大敌机的getScore()----3分
  //假设被撞对象为大黄蜂----------调用的是大黄蜂的getScore()----8分
  if(f instanceof EnemyScore){
    EnemyScore es = (EnemyScore)f;
    score += es.getScore();
  }
  //假设被撞对象为小蜜蜂----------调用的是小蜜蜂的getAwardType()
  //假设被撞对象为大黄蜂----------调用的是大黄蜂的getAwardType()
  if(f instanceof EnemyAward){
    EnemyAward ea = (EnemyAward)f;
    int type = ea.getAwardType();
    switch(type){
  case 0:
    hero.addFire();
    break;
  case 1:
    hero.addLife();
    break;
}
  }

第十天:
1、内存管理:由JVM来管理
 1)堆:
  1.1)存储new出来的对象(包括实例变量)
  1.2)垃圾:没有任何引用所指向的对象
    垃圾回收器(GC)不定时到内存中清扫垃圾,
    回收过程是透明的(看不到的),不一定一发现就立刻回收,
    通过调用System.gc()可以建议JVM尽快调度GC来回收垃圾
  1.3)实例变量的生命周期:
    创建对象时存储在堆中,对象被回收时一并被回收
  1.4)内存泄漏:不再使用的内存还没有被及时的回收,严重的泄漏会导致系统的崩溃
    建议:不再使用的对象应及时将引用设置为null

注: -------------面向对象第一天this那
只要是在方法中的都叫局部变量,main方法也是,局部变量(包括方法的参数)放在栈中,所以a是局部变量,a是引用类型变量(简称 引用)
   栈中 a是引用类型,里面装的是地址 (就是这个对象的地址)
 注:是引用类型a里装的是地址,不是栈 中装的是地址

基于a的地址 指向了Airplane对象,a=null,给a赋值为null,null表示为空,就啥也没有了,a就没有地址了,即 a没有指向任何对象,所以就认为 Airplane对象和实例变量 是垃圾
在这里插入图片描述
Airplane a1 = a;
a1是Airplane类型,a也是Airplane类型,是可以直接赋值的;因为没new,所以堆中没有东西,栈中会有个a1(a1是引用 是局部变量);
是把a里的地址存到a1中了,所以a1=0x1111(本来是a=0x1111),所以a a1它两地址的值相同,所以 它两指向了同一个对象
a=null,给a赋值为null,null表示为空,就啥也没有了,a就没有地址了,即 a没有指向任何对象
    所以此时 Airplane对象和实例变量 不是垃圾
在这里插入图片描述在这里插入图片描述

 2)栈:
  2.1)存储正在调用的方法中的局部变量(包括方法的参数)
  2.2)调用方法时,会在栈中为该方法分配一块对应的栈帧,
    栈帧中存储方法中的 局部变量和方法的参数,
    方法调用结束时,栈帧被自动清除,局部变量一并被清除
  2.3)局部变量的生命周期:
    调用方法时存储在栈中,方法调用结束时与栈帧一并被清除

注:GC回收的是堆中的对象,栈帧是被自动清除
  main的栈帧被清除了,小o也就没有了,堆中的垃圾也就被GC回收了
在这里插入图片描述

在这里插入图片描述

 3)方法区:
  3.1)存储 .class 字节码文件(包括静态变量、所有方法)
  3.2)方法只有一份通过 this来区分具体的调用对象

        (一个类只会生成一个字节码文件)
在这里插入图片描述
  Airplane a1 = new Airplane();   
① new Airplane对象时,先分配 方法区,因为第一步 先去加载Airplane这个类,一加载就把字节码文件放到方法区中
(只要用到了Airplane这个类,就在方法区中分配 Airplane.class{包括静态变量、所有方法} )

② new Airplane对象了,所以在堆中 分配new出来的对象(即 Airplane对象),包括实例变量(宽 高 speed等)

③调用main方法时,会在栈中为该方法分配一块对应的栈帧,栈帧中分配一个a1,a1装的是地址 指向 堆中的该对象

a1.y=100;
这句话一写,先找a1,就找到了地址,基于这个地址,找到堆中的Airplane这个对象,找到对象后就找到了y,把y从0改为100

  Airplane a2 = new Airplane();   
① 又一个 new Airplane对象,方法区中不改变(因为一个类只被加载一次),
② 堆中要变(因为又new对象了),所以在堆中 又分配new出来的对象(即 Airplane对象),包括实例变量(宽 高 speed等),
③ 调用main方法时,会在栈中为该方法分配一块对应的栈帧,栈帧中分配一个a2,a2装的是地址 指向 堆中的该对象

a2.y=200;
这句话一写,先找a2,就找到了地址,基于这个地址,找到堆中的Airplane这个对象,找到对象后就找到了y,把y从0改为200

a1.step(); //a1.y=102; (因为step里 y+=speed,相当于 y=y+speed,speed为2 )
a2.step(); //a2.y=202;
a1、a2 两个都调step方法了,step在内存中只有一份,a1、a2调的都是方法区中的那一份step()
那在step()中怎么区分是a1调的step() 还是a2调的step(),然后改对应的值 (a1的y改为102,a2的y改为202)?
用 this关键字区分是a1调的 还是a2调的,this关键字只能用在方法中
  a1点的时候,step()中的this指的是a1,a1的 y+=speed
  a2点的时候,step()中的this指的是a2,a2的 y+=speed


面向对象三大特征:
1.封装:
 1)类:封装的是对象的属性和行为
 2)方法:封装的是具体的业务功能实现
 3)访问控制修饰符:封装的是具体的访问权限

2.继承:
 1)作用:代码复用
 2)超类:所有派生类所共有的属性和行为
  接口:部分派生类所共有的属性和行为
  派生类:派生类所特有的属性和行为
 3)单一继承、多接口实现,具有传递性

3.多态:
 1)行为多态:所有抽象方法都是多态的,通过重写来表现多态
  对象多态:所有对象都是多态的,通过向上造型来表现多态
 2)向上造型、强制类型转换、instanceof


实例变量:
 1.类中,方法外
 2.创建对象时存储在堆中,对象被回收时一并被回收
 3.有默认值

局部变量:       面向对象第一天this那
 1.方法中
 2.调用方法时存储在栈中,方法调用结束时与栈帧一并被清除
 3.没有默认值

注:
a b c都没有赋值,但只有c会报错,变量在用之前必须声明并初始化,
程序要想走到 红色大括号{ 里面,要先调用show方法,想调show方法 要先new对象,
有了对象 才能调show
一旦new对象,在堆中分配实例变量a,同时 给a赋默认值0(即 new的时候,a已经有值了,为0)
o.show(5); 有参必须传参,即 给b赋值为5
往下走,走到c的位置,c没值,所以 c 编译错误
  所以 重点看 方法里的变量,不给值是不能用的
在这里插入图片描述


Object:所有类的鼻祖
   所有类都直接或间接的继承了Object--------API第二天/第三天详细讲解


状态有四种:
1.启动状态------------------车点着火了,但是还没有走
2.运行状态------------------车往前走着了
3.暂停状态------------------碰到红灯,暂时停止了
4.游戏结束状态---------------车熄火了


补充get/set

package oo.day10;
//补充get/set
public class GetSetDemo {
    public static void main(String[] args) {
        Student zs = new Student(); //创建了一个学生对象
       		 //给下面Student类的成员变量(属性)name和age 赋值,赋值调set,取值调get
        zs.setName("zhangsan"); //赋值
        System.out.println(zs.getName()); //取值
        zs.setAge(25);
        System.out.println(zs.getAge());
    }
}            
 
class Student{   
    private String name; //数据私有,行为公开(私有的 只能本类访问,别的类不能访问,set/get)
    private int age;
    
    public String getName(){ //get取值
        return name;
    }
    public void setName(String name){ //set赋值
        this.name = name;
    }
    public int getAge(){
        return age;
    }
    public void setAge(int age){
        this.age = age;
    }
}

 数据私有,行为公开(私有的 只能本类访问,别的类不能访问,如果想访问,会提供一套组件 get获取 / set设置)

 class Student{   
	    private String name; //数据私有,行为公开(私有的 只能本类访问,别的类不能访问,set/get)
	    private int age;

举例:
name已经私有了,外面其他类想用,做成 公开 getName(),在getName() 中 返回name(get获取,想获取一个东西,肯定要有返回值)

	 public String getName(){ //get取值
	       return name;  //获取名字 就返回名字
	 }

set设置,想赋值,就要知道赋值为几,所以需要参数(String name)

  赋值不需要返回值 不需要知道结果,把值赋上就行了,所以需要参数

	 public void setName(String name){ //set赋值
		    this.name = name; //赋值
	 }

         可以认为 set在赋值,get在取值
        get是有返回值的,set是有参数的
   对于一个私有的数据(如:name),一般会公开 set 和 get 这一套,让别人用

怎么用:
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值