16、Inner Class(内部类)

一、Note

1、意义

  • 为了更好的封装

    • 为了实现更好的封装,普通类(非内部类)的访问修饰符不能为private或protected,而内部类可以。所以当我们将内部类声明为private时,就可对外隐藏内部类
  • 使Java的多继承机制变得更加完善

  • 每个内部类都能独立的继承或实现其他的类或接口,所以无论外部类是否已经继承或实现其他的类或接口,对于内部类都没有影响,内部类使得多继承的解决方案变得完整。

  • 方便编写线程代码

  • 方便编写事件驱动程序

  • 方便将存在一定逻辑关系的类组织在一起,又可以对外界隐藏

2、分类

2.1、按照是否是成员来划分

(按照是否在类体括号中声明来划分)

  • Nested Classes(嵌套类)

    • 静态嵌套类(静态内部类)
    • 实例嵌套类(实例内部类)
  • Local Classes(局部类)

    • 局部内部类
    • 匿名内部类
  • 嵌套类和局部类最重要的界限是作用域不同

    • 嵌套类的作用域是属于类的,嵌套类属于类的成员
    • 局部类的作用域是局部的
    • 所有的匿名内部类都是局部的
2.2、Java语言规范中的分类
  • 嵌套类

    • 静态嵌套类(被static修饰的成员内部类)
  • 内部类

    • 实例内部类

      • 没有被static修饰的成员内部类
      • 除了常量外,不能声明static修饰的字段;以及方法
    • 因此,实例内部类里可以定义静态变量[相当于常量]

  • 局部类

    • 在宿主类类体括号内部的某个局部区域中声明的内部类

    • 除了常量外,不能声明static修饰的字段;以及方法

      • 作用域是局部的
    • 匿名类

      • 不能显式指定类名
      • 因此,也不能显式声明构造方法
      • 但是创建匿名类的实例时,的确会调用父类的指定构造方法
      • 但是通过javac反编译匿名类得到结果可知,匿名类是有构造方法的,只是不公开
      • 除了常量外,不能声明static修饰的字段

3、声明

3.1、静态内部类(静态嵌套类)
public class Computer { 
	static class Brand { //在类中写,有static修饰
        static int x;
	}                                     
}
3.2、实例内部类(非静态嵌套类)
public class Computer { 
	class Brand { //在类中写,没有static修饰
        //static int x;
        final static int x;
	}                                     
}
//内部类未加修饰符时,就是默认修饰符,包私有,外部类也可访问此内部类
3.3、局部内部类
  • 出了类之后就无法使用,有局部作用域的限制
public class Computer { 
	public void show() {
    	class Printer { 
            //static int x;
            //static void hello(){}
            //final static void hello(){}
            final static int x;
    	}                                  
 	}                               
 }
或者:
public class Compuer{
    {
        class Printer{
         
        }
    }
}
3.4、匿名内部类
package cn.edu.ecut;
public class Computer{
    public void welcome(){
        // 以{}作为类体,继承Object类
		Object o = new Object() {   
    		//只有类体括号,不能显式指定类名  
		}
        System.out.println(o.getClass());
    }
    public static void main(String[] args){
        Computer c = new Computer();
        c.welcome();//输出:class cn.edu.ecut.Computer$1
    }

3.5、注意
  • 局部内部类和匿名内部类绝不会有static修饰,因为它们不是属于类的,它们属于局部代码块

4、字节码文件的名称

  • 嵌套类对应的字节码文件名称是 外部类类名$内部类类名.class
  • 局部内部类对应的字节码文件名称是 外部类类名$N内部类类名.class
    • 数字N指的是局部内部类在外部类的方法或代码块中出现的次数(顺序)
  • 匿名内部类对应的字节码文件名称是 外部类类名$N.class
    • 数字N指的是不同局部内部类在外部类的方法或代码块中出现的顺序
  • 注意:文件名把.class去掉,剩下的其实就是类名

5、声明内部类类型变量

  • 对于嵌套类来说,使用 外部类类名.内部类类名 来指定变量类型

    • 如:Computer.Brand brand = null ;
  • 对于局部类来说,只能在其作用域内部直接使用其类名来声明

    • 如:Printer p = null ;
  • 对于匿名内部类来说,因为不能显式指定匿名类的类名,所以也就不能声明匿名类类型的引用变量,但是可以声明匿名类所实现的接口类型的引用变量 或 匿名类所继承父类类型的引用变量

6、创建内部类的实例

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

    • 使用 new 外部类类名.内部类类名(参数) 形式来创建其实例
    • 如:Computer.Brand b = new Computer.Brand();
  • 实例内部类(非静态嵌套类)

    • 首先需要创建外部类类型的实例(比如Computer c = new Computer();)
    • 然后再在外部类的实例的基础上来创建实例内部类的实例(比如Computer.Mainboard m = c.new Minboard())
    • 即:Computer.Mainboard m = null; m = new Computer().new Mainboard();
  • 局部内部类

    • 直接在 其有效作用域{} 中使用new关键字创建即可
  • 匿名内部类

    • 创建实现了某个接口的匿名类的实例

      • 此时匿名类的直接父类是Object类
      • 此时匿名类实现了指定的接口(单个)
    • 创建继承了某个抽象类并实现其中所有抽象方法的匿名类的实例

      • 匿名类的直接父类就是相应的抽象类
      • 在创建匿名类实例时可以调用父类中带参数的构造方法
    • 创建继承了某个具体类的匿名类的实例

      • 匿名类的直接父类就是相应的具体类
      • 可以在匿名类中重写被继承类中的某些方法(根据需求来确定)
      • 在创建匿名类实例时可以调用父类中带参数的构造方法

7、不关注内部类的继承

  • 内部类主要是为了封装

二、Code

1、理解嵌套内部类和局部内部类的区别

Computer.java
package cn.edu.ecut;

// Computer 是个外部类,它对应的 字节码 文件是 Computer.class
public class Computer { // 电脑
	
	// 直接在 外部类 的 类体括号中声明的 类,就是 嵌套类 ( Nested Classes )
	// 嵌套类 也被称作 成员内部类

	// 有 static 修饰的 嵌套类,也被称作 静态嵌套类 或 静态内部类
	static class Brand { // 铭牌
		// 对应的 字节码 文件名称是 Computer$Brand.class
	}
	
	// 没有 static 修饰的 嵌套类,也被称作 非静态嵌套类 或 非静态内部类 或 实例嵌套类 或 实例内部类
	class Mainboard { // 主板
		// 对应的 字节码 文件名称是 Computer$Mainboard.class
        //static int x;//错误,因为x是类加载时就创建的,但是Mainboard内部类是创造一个类对象时才创建的
        final static int x = 100; //定义一个常量是可以的
	}
	
	public void show() {
		
		// 局部内部类
		class Printer { 
			// 对应的 字节码 文件名称是 Computer$1Printer.class
		}
		
		Printer p = new Printer();
		System.out.println( p );
		
	}
	
	public void hello() {
		
		// 局部内部类
		class Printer { 
			// 对应的 字节码 文件名称是 Computer$2Printer.class
		}
		
		Printer p = new Printer();
		System.out.println( p );
		
	}
    
    //Object o = new Object() { };// 若放在类{}里面,也可以,但此时它不是成员内部类,它仍然是局部内部类,因为它只能用一次
	
	public void welcome() {
		Object o = new Object() { 
			// 只有类体括号、不能显式指定类名的局部内部类-匿名内部类
		};
		System.out.println( o.getClass() );
	}
    
    public void baby() {
		Object o = new Object() { 
		};
		System.out.println( o.getClass() );
	}
	
	public static void main(String[] args) {
		Computer c = new Computer();
		c.welcome(); // 输出:class cn.edu.ecut.Computer$1
        c.welcome(); //class cn.edu.ecut.Computer$1
        c.baby(); //class cn.edu.ecut.Computer$2

	}
    
    

}
ComputerTest.java
package cn.edu.ecut;

public class ComputerTest {

	public static void main(String[] args) {
		
		// 声明 静态嵌套类 ( 静态内部类 ) 的 引用变量
		Computer.Brand b =  null ;
		
		// 创建 静态嵌套类 ( 静态内部类 ) 的实例
		b = new Computer.Brand();
		System.out.println( b );
		
		// 获得 Brand 类的全限定名称
		System.out.println( b.getClass().getName() );
		
		// 获得 Brand 类的规范化名称(规范化类名),以原点隔开。对以后类加载有用(毕竟类加载的是类,不是文件==>把全限定名称即文件名中的$换为.)
		System.out.println( b.getClass().getCanonicalName() );
		
		System.out.println( "~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~" );
        
        // m所指向的对象包围在c的对象内部,称作包围实例;
        // Computer.Mainboard m= new Computer().new Mainboard();
		
		// 声明 非静态嵌套类 ( 实例内部类 ) 的 引用变量
		Computer.Mainboard m = null ;
		
        // 创建 非静态嵌套类 ( 实例内部类 ) 的实例
		Computer c = new Computer() ; // 首先创建外部类的实例
		m = c.new Mainboard(); // 在 外部类实例 基础之上创建 实例内部类 实例
        
		System.out.println( m ); //m.toString()形式,即m指向的对象的【类型(包名.类名)@地址】
		System.out.println( m.getClass().getName() ); // 全限定名称
		System.out.println( m.getClass().getCanonicalName() ); //规范化名称

	}

}

输出:

cn.edu.ecut.Computer$Brand@4d591d15
cn.edu.ecut.Computer$Brand
cn.edu.ecut.Computer.Brand
~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
cn.edu.ecut.Computer$Mainboard@6a6824be 
cn.edu.ecut.Computer$Mainboard
cn.edu.ecut.Computer.Mainboard

2、在实例内部类中使用 外部类类名.this 来引用外部类的实例

CellPhone.java
package cn.edu.ecut;

public class CellPhone {
	
	public String brand = "一加" ;
	
	public class Screen {
		
		String brand = "京东方" ;
		
		public void show() {
            String brand ="";
			// 在 实例内部类 中 使用 this 可以引用 该实例内部类的当前实例
            // 有this.则brand指的是“京东方”;没有this.则brand指的是“”
			System.out.println( this.brand );
			// 通过 "外部类类名.this.字段名" 来引用外部类的实例变量
			System.out.println( CellPhone.this.brand );
		}
		
		public CellPhone getOuter() {
			// 在 实例内部类 中 可以使用 "外部类类名.this" 来引用 外部类的 当前实例
			return CellPhone.this ;
		}		
	} // end : Screen
    
    public static class A{
         public void show(){
             //System.out.println(CellPhone.this); // 错误
             //静态内部类只能访问静态成员,不能访问外部类的实例(非静态成员);实例内部类可以访问外部类的实例,不能访问静态成员
		}
    }
    /*
    static class B{
        CellPhone p = new CellPhone();
    }
    此代码有些问题,若没有 static,则更加极有可能造成内存溢出,因为在创建实例(new CellPhone())时,就会产生B这个类,但无法确定产生多少B这个类
    class B{
    	CellPhone p = new CellPhone();
    }
    */

}
CellPhoneTest.java
package cn.edu.ecut;

public class CellPhoneTest {

	public static void main(String[] args) {
		
		/*
		// 引用变量 p 指向了 一个 CellPhone 类型的实例
		CellPhone p = new CellPhone();
		// 引用变量 s 指向了一个 Screen 类型的实例
		CellPhone.Screen s = p.new Screen();
		*/
		
		// 创建 实例内部类 Screen 类的实例
		CellPhone.Screen s = new CellPhone().new Screen(); // 标记
		System.out.println( s );
		s.show();
        
		// 用p接收外部类 CellPhone的实例
		CellPhone p = s.getOuter(); // 此处的p不是重新new出来的,是在“标记”这行new的这两个实例时就存在的,当时创建外部类Cellphone的实例是什么,p就接收什么
		System.out.println( p );

	}

}

输出:

cn.edu.ecut.CellPhone$Screen@65ae6ba4
京东方
一加
cn.edu.ecut.CellPhone@7960847b

3、创建实现了某个接口的匿名类的实例

Flyable.java
package cn.edu.ecut;

public interface Flyable {
	
	void fly();

}
AnonymousClassTest1.java
package cn.edu.ecut;

import java.util.Arrays;

/**
 * 【最常见的应用】使用 匿名类 实现 接口,并创建该类的实例
 */
public class AnonymousClassTest1 {

	public static void main(String[] args) {
		
		// 声明 Flyable 类型的引用变量,并将 null 赋值给该变量
		Flyable f = null ; // 引用变量 f  的 编译时类型 是 Flyable
		 
		// f = new Flyable() ; // 接口都没有构造方法,也不能被实例化
		
		// 形式上,似乎是在通过 new 关键字 创建 Flyable 接口的实例 【尸体】
		// 本质上,创建一个实现了 Flyable 接口的 匿名类 的实例  【借尸还魂】
        // {}作为一个类体,实现了接口,实现了接口里的抽象方法
		f = new Flyable() { // 匿名类开始   【魂】
			@Override
			public void fly() { // 在匿名类中实现 Flyable 接口中所有的抽象方法
				System.out.println( "飞飞飞,在梦里飞,想飞哪里飞哪里" );
			}
		}; // 匿名类结束
        
		
		f.fly();
        
        /*
//匿名内部类只使用这一次;若想再次使用这个类的实例,则需要再写一个匿名类,或者可以重新声明一个类实现接口
        	f = new Flyable() { 
			@Override
			public void fly() { 
				System.out.println( "飞飞飞,在梦里飞,想飞哪里飞哪里" );
			}
		}; 
		//若是再写了一个匿名类,则此时字节码文件有:
		//AnonymousClassTest1$1.class,AnonymousClassTest1$2.class
        */
		
		Class<?> c = f.getClass() ;  // 获得运行时类型
		System.out.println( c.getName() ); // 全限定名称-cn.edu.ecut.AnonymousClassTest1$1
		System.out.println( c.getCanonicalName() ); // 规范化类名 ( 注意这里是 null )
        // 匿名内部类不能显式书写类名,因此输出类名为null
		
		Class<?> p = c.getSuperclass(); // 获得 c 所表示类型的父类
		System.out.println( p.getName() ); //java.lang.Object
		
        // 获得 c 所表示的类所实现的接口 (或 c所表示的接口 所继承的接口)
		Class<?>[] interfaces = c.getInterfaces(); 
		System.out.println( Arrays.toString( interfaces ) ); 
        //此处调用的是Arrays/toString(Object[] a),Class是Object的子类,因此可直接写Class类型的
        
	}

}

输出:

飞飞飞,在梦里飞,想飞哪里飞哪里
cn.edu.ecut.AnonymousClassTest1$1
null
java.lang.Object
[interface cn.edu.ecut.Flyable]

4、创建继承了某个抽象类并实现其中所有抽象方法的匿名类的实例

Human.java
package cn.edu.ecut;

public abstract class Human {
	
	public String name ;
	
	public Human() {
		super();
		System.out.println( "Human()" );
	}
	
	public Human( String name ) {
		super();
		this.name = name ;
		System.out.println( "Human( String )" );
	}
	
	public abstract void eat( String food );

}
AnonymousClassTest2.java
package cn.edu.ecut;

/**
 * 创建继承了某个抽象类并实现其中所有抽象方法的匿名类的实例
 */
public class AnonymousClassTest2 {

	public static void main(String[] args) {
		
		Human h = null ;
		
		// h = new Human( "张三丰" ); // 抽象类有构造方法,但是抽象类不能实例化
		
		// 形式上,似乎是在通过 new 关键字 创建抽象类 Human 的实例 【尸体】
		// 本质上,创建一个继承了 Human 类 并实现了其中所有抽象方法的 匿名类 的实例  【借尸还魂】
		h = new Human(  "张三丰" ) { // 匿名类开始 【魂】
			@Override
			public void eat(String food) { // 重写抽象类中的抽象方法
				System.out.println( this.name + "吃" + food );
			} 
		} ; // 匿名类结束
		
		h.eat( "西瓜" );
		
		System.out.println( "~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~" );
		
		h = new Human() {
			@Override
			public void eat(String food) {
				System.out.println( this.name + "吃" + food );
			} 
		};
		h.name = "张君宝" ;
		h.eat( "冬瓜" );
		
	}

}

输出:

Human( String )
张三丰吃西瓜
~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
Human()
张君宝吃冬瓜

5、创建继承了某个具体类的匿名类的实例

Aircraft.java
package cn.edu.ecut;

public class Aircraft {
	
	public String type ;
	
	public Aircraft(){
		super();
	}
	
	public Aircraft( String type ){
		super();
		this.type = type ;
	}
	
	public void fly() {
		System.out.println( "飞行中..." );
	}
	
	public void travel() {
		System.out.println( "行驶中..." );
	}

}
AnonymousClassTest3.java
package cn.edu.ecut;

import java.util.Arrays;

/**
 * 1、创建继承了某个具体类的匿名类实例
 * 2、可以在匿名类中重写被继承类中的某些方法
 */
public class AnonymousClassTest3 {

	public static void main(String[] args) {
		
		Aircraft a = new Aircraft();
		a.fly();
		a.travel();
		
		System.out.println( "~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~" );
		
		Aircraft b = new Aircraft() { } ; // 父类引用 指向 子类对象 ( 因为最后的 { } 就是一个匿名类的类体 ),子类即为匿名类
		System.out.println( b.getClass() );
		
		System.out.println( "~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~" );
		
		// 调用 父类无参数构造方法
		Aircraft c = new Aircraft() {
			// 在匿名类中声明实例变量
			String name = "航天飞机" ;
			@Override
			public void fly() { // 重写从父类 中继承的 fly 方法
				System.out.println( this.name + "正在飞行" );
			}
		} ;
		
		c.fly(); // 调用重写后的方法
        System.out.println( c.getClass() );
		
		System.out.println( "~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~" );
		
		// 调用 父类有参数构造方法
		Aircraft d = new Aircraft( "飞船" ) {
			// 在匿名类中声明实例变量
			String name = "嫦娥二号" ;
			@Override
			public void fly() { // 重写从父类 中继承的 fly 方法
				System.out.println( this.name + this.type + "正在月球上空飞行" );
			}
		} ;
		
		d.fly(); // 调用重写后的方法
		
		System.out.println( "~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~" );
		
		Class<?> cc = d.getClass() ;  // 获得运行时类型
		System.out.println( cc.getName() ); // 全限定名称
		System.out.println( cc.getCanonicalName() ); // 规范化类名 ( 注意这里是 null )
		
		Class<?> p = cc.getSuperclass(); // 获得 cc 所表示类型的父类
		System.out.println( p.getName() );
		
		Class<?>[] interfaces = cc.getInterfaces(); // 获得 cc 所表示的类所实现的接口
		System.out.println( Arrays.toString( interfaces ) );

	}

}

输出:

飞行中...
行驶中...
~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
class cn.edu.ecut.AnonymousClassTest3$1
~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
航天飞机正在飞行
class cn.edu.ecut.AnonymousClassTest3$2
~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
嫦娥二号飞船正在月球上空飞行
~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
cn.edu.ecut.AnonymousClassTest3$3
null //匿名类在语言规范中不能显式书写类名,因此输出null
cn.edu.ecut.Aircraft
[] //说明没有实现任何接口,因为匿名类直接继承了父类Aircraft.java

6、理解匿名类不能显式声明构造方法(但匿名类是有构造方法的)

AnonymousClassTest4.java
package cn.edu.ecut;

import java.lang.reflect.Constructor;
import java.util.Arrays;

/**
 * 尝试获取匿名类的构造方法
 */
public class AnonymousClassTest4 {

	public static void main(String[] args) {
		
		// 在 java.lang.Cloneable 接口中没有声明任何方法
		Cloneable c = new Cloneable() { // 创建 实现了接口Cloneable的 匿名类实例
			// 这里什么都不用写
		};
		
		/*
		// {}作为一个类体,继承了父类Human,调用父类Human中的带参数构造(通过匿名类中的带参构造调用父类构造,匿名类中的带参构造中第一行为:super("张翠山");,只不过匿名类中的构造对我们不可见)
		Human c = new Human( "张翠山" ) { // 创建 继承了具体类Human的 匿名类实例
			@Override
			public void eat(String food) { 
			}
		};
		*/
		
		System.out.println( c );
		
		System.out.println( "~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~" );
		
		Class<?> clazz = c.getClass();
		
		// 通过 反射 来获取 匿名类 中所有的构造方法组成的数组
		Constructor<?>[] constructors = clazz.getDeclaredConstructors();
		
		System.out.println( Arrays.toString( constructors ) ); 

	}

}

输出:

cn.edu.ecut.AnonymousClassTest4$1@4d591d15
~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
[cn.edu.ecut.AnonymousClassTest4$1()]  //获取到一个无参的构造

    若换成注释里的内容,并且将Cloneable一行注释,则输出:
Human( String )
cn.edu.ecut.AnonymousClassTest4$1@4aa8f0b4
~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
[cn.edu.ecut.AnonymousClassTest4$1(java.lang.String)]

7、匿名内部类应用举例:在实参中传入匿名类的实例

Printable.java
package cn.edu.ecut;

// 打印接口
public interface Printable {
	
	// 打印
	void print( String content );
	
	// 复印
	String copy( String content );

}
Store.java
package cn.edu.ecut;

public class Store {

	public void print( Printable p , String content ) {
		p.print(content); // 这里调用的是 Printable 实例的 print 方法
	}
	
	public String copy( Printable p , String content ) {
		return p.copy(content) ; // 这里调用的是 Printable 实例的 copy 方法
	}
	
}
StoreTest.java
package cn.edu.ecut;

public class StoreTest {

	public static void main(String[] args) {
		
		Store s = new Store();
		
        // 方法一:传入匿名类实例
		Printable p = new Printable() { // 引用变量p指向 实现了接口Printable的 匿名内部类的对象
			
            String name = "HP打印机" ; // 匿名类的实例变量
            
			@Override
			public void print(String content) {
				System.out.println( this.name + " 正在打印 " + content );
			}
			
			@Override
			public String copy(String content) {
				System.out.println( this.name + " 正在复印 " + content );
				String s = new String( content );
				return s ;
			}
		};
		
		s.print( p , "犯我中华者虽远必诛" );
		
        // 方法二:传入匿名类实例
		s.print( new Printable() {
			String name = "爱普生打印机" ; // 匿名类的实例变量
			@Override
			public void print(String content) {
				System.out.println( this.name + " 正在打印 " + content );
			}
			
			@Override
			public String copy(String content) {
				System.out.println( this.name + " 正在复印 " + content );
				String s = new String( content );
				return s ;
			}
		} ,  "犯我中华者虽远必诛" );

	}

}

输出:

HP打印机 正在打印 犯我中华者虽远必诛
爱普生打印机 正在打印 犯我中华者虽远必诛
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值