Java自学参考总结

DOS 

打开方式

1.直接搜cmd 

2.开始  windows 系统中  命令提示符

3.Windows+R ,输入cmd 

命令

dir  查看当前目录下的文件及文件夹

md  创建目录

rd  删除目录

cd ..  切换到上一级

cd \  切换到根目录

E:  切换到E 盘

del  删除文件*.txt 

exit  退出dos 命令行

cls  清屏

 

JRE  JDK 

JRE java运行环境 包括JVM和核心类库

JDK  java开发工具包

使用JDK开发完成的java程序,交给JRE去执行

.java文件  –编译javac.exe –>  .class文件–  运行java.exe –>  结果

语言特点   跨平台性  

 

环境配置

1.在系统变量中添加JAVA_HOME  C:\Program Files\Java\jdk1.8.0_181

2.添加系统变量CLASSPATH     .;%JAVA_HOME%\lib\dt.jar;%JAVA_HOME%\lib\tools.jar;  (注意开始的. 和末尾的;)

3.修改PATH路径  在后面加  %JAVA_HOME%\bin;%JAVA_HOME%\jre\bin;

临时配置环境变量

set  PATH=   ..  /bin   只在当前窗口有效

 

注释  

作用 1.解释说明 2.调试程序

单行//      多行/*  */  (不可嵌套)

文档注释符 /**  */

 

标识符 

以字母,下划线,$,汉字开头,后面可跟字母数字_和$,区分大小写

关键字 :全小写字母

包名 :  全小写

类名 接口名  :XxxYyyZzz

函数名  变量名 :xxxYyyZzz

常量名 :XXXYYYZZZ

保留字:java内部定义的名称术语

   true  null false为小写,const不是java中的保留字

 

常量(final):在程序运行过程中永远不会改变的量 

变量:  在程序运行过程中可以改变的量

[修饰符] 类型 变量 [=表达式]{,变量[=表达式]};

 同一作用域内不能重复声明同一变量,但可以先声明,后赋值。

 按声明位置分:

     成员变量(全局变量) :  类之内,方法之外

     局部变量                  :  方法之内

  作用范围:  成员 整个类中    局部  函数或语句

  在内存中位置:  成员 在堆内存中,因为对象的存在,才在内存中存在   

                局部  存在栈内存中

 整数表现形式

   十六进制  0~9  A~F或a~f  以0x或0X开头(零)

   八进制   0~7  以0开头

   十进制

   二进制

 

数据类型:

   基本数据类型:(在栈空间中为变量分配空间,并将数据存储在此空间中)

        数值型    整型:byte    1(字节数)   -2^7~2^7-1     -128~127 0

                         short          2    -2^15~2^15-1 -32768~32767       0                                           

                          int             4     -2147483648 ~2147483647            -2^31~2^31-1 0

                         long            8  -2^63~2^63-1(加l )   0L

                浮点型 :float  4   -3.403E-38~3.403E38(加f )   0.0F(6~7位有效数字)

                        double   8   -1.798E-308~1.798E308       0.0D(15位有效数字)(默认)

        字符型   char ‘\u0000’ 或'\0' 

        布尔型   boolean    true /false      false

引用数据类型:

(在堆中分配空间来存储实际数据,在栈中分配空间存储数据在堆中的地址)

       类   接口    数组

    转换原则

    byte →short→ int→ long →float→double

    char→  int

    强制转换   byte  a=2;

                  a=(byte)(a+3);

转义字符 

  \n    \b退格   \r回车(Windows系统,回车符是\r\n)    \t制表符

 

运算符

 算术运算符

  +-*/% ++ -- 

  b=a++;  先赋值   后加一

  b=++a;  先加一  后赋值

  整数运算的结果至少为int型。

 赋值运算符

左边的操作数必须是变量,右边的操作数必须是表达式。(自右向左)

   = += -= *= /= %=

   short  s=4;

   s+=5;//自动转换   只赋值

 逻辑运算符

  &  |  ^  !  &&  ||  

   ^ 同真或同假为假,两边不同为真

       &与&&的区别 

   & 无论左边真假,右边都进行运算

   &&  只有左边为真时,右边才进行运算

  7^4^4=7

  互换值:  n=3 m=8

  ①n=n+m;      ②n=n^m;

      m=n-m;         m=n^m;

       n=n-m;                  n=n^m:

  关系运算符

 任何数据类型的数据(包括基本数据类型和引用数据类型)都可以通过==或!=来判断是否相等。

  == !=  <  >  <=  >=  

 条件运算符 

 表达式1?表达式2:表达式3;

  ( 弊端:运算完必须有结果)

   ?: 的结合顺序是由右向左

   位运算符

~ & | ^ >>  << >>>

  6>>2   6/2²=1

  3<<2   3*2²=12

  >>   原有数据最高位决定补什么

  >>>  无论最高位是什么都补零

   

流程控制语句

循环结构

for    while    do ...while 

 初始部分  循环条件 循环体   迭代部分

定义在for中的增量执行完后内存释放

分支结构

if...else ..

 switch  (整型或字符型表达式)

{

   case 整型或字符型常量1: 语句1;[break;]

   default : 语句n +1;

}

byte  short  int  char    要求判断的具体数值不同

break 和continue 

break  跳出   用于switch、循环  

continue 继续   用于循环

break和continue单独存在时,下面不可以有任何语句,执行不到

 

面向对象是基于面向过程的。

面向过程强调功能,面向对象是将功能封装进对象,强调具备了功能的对象。

将复杂问题简单化,由执行者变成指挥者。

 

类与对象的关系

类是对现实生活中某一事物的描述

对象是实际存在的事物的个体

类是创建对象的模板

描述就是class定义的类,其实是在描述事物的属性和行为,属性对应类中变量,行为对应类中函数,共称为类中成员。具体对象就是对应java在堆内存中用new建立的实体。

类声明格式:

[访问权限] [abstract] [final] class 类名 <泛型> [extends 父类] [implements 接口列表]

 

成员变量 成员方法

[访问权限] [static] [final] [transient] 数据类型  变量名1[=变量值]{,变量名[ =变量值]};

transient 用于声明非持久化或序列化的变量

对象的默认初值为空null,局部变量没有默认初值

[访问权限] [static] [final] 返回类型  方法名称 (参数1,参数2...)[throws  Execption ]

{    语句体   }

如何定义函数?

明确该功能的运算结果  确定返回值类型

判断是否需要未知的内容参与运算  确定参数类型和参数个数

 

修饰符

类的访问权限 public   default  

成员的访问权限 

 public  

private      本类

protected  任何子类,包内  (类与实例成员都可以继承,类成员可以用对象调用,但实例成员用对象不可调用,用super可以调用)

default       包内

    

创建对象包括声明对象和实例化对象即为对象分配存储空间

对象使用:对象名.对象成员(变量和方法)

对象存储:堆 先进先出   栈  先进后出

内存结构

栈内存  用于存储局部变量,当数据使用完,所占空间自动释放

堆内存  数组和对象,通过new建立的实例都存放在堆内存中

  每一个实体都有内存地址值;实体中的变量都有默认初始化值;实体不再使用时,会不定期被垃圾回收器回收。

 

   equals ()比较引用数据类型

     ==          比较基本数据类型

 

匿名对象  只有实现 没有声明  new car()

  当对对象的方法只调用一次或者作为实际对象进行传递时使用匿名对象

 

方法参数传递 : 基本类型传递  引用类型传递

如果形参是基本数据类型,则实参向形参传递的是值的副本;如果形参是引用数据类型,则传递的是引用值或地址值或对象的名字而不是对象本身。

 

封装  

隐藏对象的属性和实现细节,仅对外提供公共的访问方式setAge  getAge

   好处:将变化隔离,便于使用,提高重用性和安全性

    private  私有是封装的一种表现形式。权限修饰符,修饰类中成员,只在本类中有效。

 

继承  

好处:实现代码复用  提高编程效率

弊端:造成了父类子类之间的强耦合

继承父类的成员变量和方法,不继承构造方法和私有成员,不继承final修饰的方法,不继承父类中被隐藏的成员变量和被覆盖的成员方法;

 必须是类与类之间有所属关系才可以继承,只支持单继承,多继承容易带来安全隐患。支持多层继承,是一个继承体系。(当多个父类定义了相同功能时,当功能内容不同时,子类对象不确定要运行哪一个)

如何使用一个继承体系中的功能?先查阅体系中父类的描述,父类中定义的是该体系中的共性功能,在具体调用时,要创建最子类的对象。(有可能父类不能创建对象;创建子类对象可以使用更多的功能,包括基本的和特有的)

子父类中的变量:

如果子类出现非私有的同名成员变量时,子类要访问本类中的变量用this,访问父类中的同名变量用super

super代表父类对象的引用,this代表本类对象的引用

子父类中的函数:

子类出现与父类一模一样的函数时,当子类对象调用该函数时,会运行子类函数的内容,此时父类的函数被重写。

子父类中的构造函数:

在对子类对象进行初始化时,父类构造函数也会运行,子类构造函数默认第一行有一条隐式语句super(),会访问父类中空参数的构造函数,且子类所有构造函数的第一行默认都是super()。

(父类中的数据子类可以直接获取,子类对象在建立时需要先查看父类对象是如何对这些数据进行初始化的,所以子类一定要访问父类中的构造函数)

若要访问父类中指定的构造函数,可以手动定义super语句来指定。super语句一定定义在子类构造函数的第一行,子类构造函数第一行也可以手动指定this语句来访文本类中的构造函数。子类中至少会有一个构造函数会访问父类中构造函数。

 

super引用

super表示当前对象的父类,是对当前对象的父类对象的引用。

用法①调用父类的构造方法

       ②引用父类同名成员,被覆盖的实例方法,被隐藏的成员变量和静态方法

super不能像this一样单独使用,但都不能用在静态方法中。

super只在子类中使用,super()一般用于子类构造方法的第一句

 

final  最终

修饰类  变量 对象  方法

修饰的类不可以被继承,避免被子类复写;修饰的方法不可以被复写;修饰成员变量时,可以理解为常量,必须赋以初值且只能赋值一次,所有字母都大写,多个单词间用'_'连接,修饰局部变量时,该局部变量只能被赋值一次;修饰方法中的参数时,若为基本数据类型,该值不能被改变,若是对象类型,则对象的引用不可变;内部类定义在类中的局部位置上只能访问该局部被final修饰的局部变量。

 

多态      事物的多种表现形态

在程序中同一符号或名字在不同情况下具有不同的语义解释。

多态至始至终都是子类对象在做着变化

体现:父类的引用指向了自己的子类对象

前提:类与类之间有关系  实现/继承

好处:提高了代码的拓展性,使得代码易于维护和扩充

弊端:只能使用父类的引用来调用父类的成员

 

向上转型  Animal a=new Cat() 类型提升

声明一个父类类型的变量,但将子类的实例赋给了他。父类变量指向了子类对象

①使用子类强大的功能②抽取父类的共性

instanceof 判断一个对象是否属于指定类及其子类

子类隐藏父类的成员变量和静态方法,上转型对象引用的是父类的;

子类覆盖父类的实例方法,上转型对象引用的是子类的。(动态绑定:在运行时确定要调用父类还是子类)

private,static,final修饰的成员都属于静态绑定,在编译时确定要调用父类还是子类。

覆盖的方法不应有更窄的访问权限,不应产生更多的异常;静态方法和最终方法不能被覆盖。

 

向下转型 Cat c=(cat) a  想要调用子类特有方法,强制将父类的引用转成子类类型。

父类引用指向自己的子类对象时,该引用可以被提升,也可以被强制转换

Animal a=new Animal(); Cat c=(Cat) a;  (×)

成员函数的特点:编译时,参阅引用型变量(animal)所属的类中是否有调用的方法,若有编译通过,没有编译失败;运行时,参阅对象所属的类(cat)中是否有调用的方法。编译看左边,运行看右边。

成员变量的特点:编译和运行都参考左边(应用型变量所属的类)

静态成员函数的特点:同成员变量

 

重写(覆盖)

方法的覆盖,方法名相同,但但父类对象调用时,调用的是父类的方法,子类调用的是子类的方法。

当子类继承父类,沿袭了父类的功能到子类中,但是子类虽具备该功能,但是功能的内容却和子类不一样,这时没有必要定义新功能,而是使用覆盖特殊,保留父类的功能定义,并重写功能内容。

子类覆盖父类,必须保证子类权限大于等于父类权限才可以覆盖,否则编译失败;静态只能覆盖静态。

覆盖的特点:

覆盖方法的标志必须要和被覆盖的方法的标志完全匹配;返回值必须一致;所抛出的异常一致或是其子类;被覆盖的方法不能为private

 

重载

方法的重载,方法名相同但随着调用它的对象不同,方法的含义不同。

与返回值类型无关,只与参数列表中参数类型和参数个数有关

当定义的功能相同时,但参与运算的未知内容不同,这时就定义一个函数名称以表示其功能,方便阅读 而通过参数列表的不同来区分。

重载的特点:

重载只能通过不同的参数样式;不能通过访问权限 返回类型 抛出异常进行重载;;方法的异常和数目不会对重载造成影响;private修饰的父类方法不能被子类重载;main方法也可以被子类重载。

拷贝构造方法:由同一个类的已知对象创建新对象的构造方法

  Cat cat2=new Cat(cat1);

 

覆盖和重载

相同点:

 都要求方法名相同;都可以用于抽象方法和非抽象方法之间

不同点:

 ①覆盖要求参数签名必须一致;重载要求必须不一致

 ②覆盖返回类型必须一致;重载不做限制

 ③覆盖只能用于子类覆盖父类的方法;重载用于同一个类中的所有方法

 ④覆盖对方法的访问权限和抛出异常有特殊要求;重载没有限制

 ⑤父类的一个方法只能被子类覆盖一次;而一个方法在所在类中可被重载多次

 

 

构造方法 

① 创建对象   ② 初始化成员变量

函数名与类名相同 不用定义返回值类型  不能写return语句  

     作用:对类的成员变量进行初始化工作。

该事物存在就具备一些特性或者行为时,定义构造函数。

所有类中都有一个默认的无参构造方法;当自定义构造方法后,系统将不再提供默认构造函数;默认构造函数权限和所属类一致

 

构造函数与一般函数的不同:

①写法上  名称不同

②运行上   构造函数是对象一建立就运行,给对象初始化;一般函数是对象调用时才执行,给对象添加功能。一对象建立,构造函数只运行一次,而一般方法可以被该对象调用多次。

③作用不同 成员方法实现对类中成员变量的操作,构造方法用于创建类的实例并对实例的成员变量进行初始化。

④调用方式不同  成员方法通过对象或类调用或直接调用,构造方法通过new运算符调用。

构造函数内可以调用构造函数,而其他函数不能调用构造函数。

 

构造代码块   定义共性内容

对象一建立就运行,优先于构造函数

构造代码块是给所有对象进行统一初始化,构造函数是给对应对象初始化

 

数组

一维数组

声明:type  [] 变量名=new  type [数组中元素的个数]

type  [] 变量名={逗号分隔的初始化值}

初始化:动态初始化 :定义 分配空间与赋值分开

静态初始化 :定义的同时分配空间并复制

默认初始化

二维数组

声明:type  [] []变量名=new  type [数组中元素的个数][数组中元素的个数]

type  [] []变量名={逗号分隔的初始化值}

 

算法

选择排序

插入排序

冒泡排序

希尔排序

折半查找

递归

 

this 关键字

区别于局部变量与成员变量同名的情况

this代表本类对象,代表它所在函数所属对象的引用。哪个对象在调用this所在函数,this就代表哪个对象。

应用: 当定义类中功能时,该函数内部要用到调用该函数的对象时,这时用this来表示。

this语句用于构造函数间互相调用,只能放在构造函数第一行,初始化动作先执行。

①在实例方法中,作为本类当前对象将其引用作为自变量传递给其他方法

②在实例方法中,引用本类当前对象的成员变量和成员方法

③调用本类重载的构造方法

 

static关键字

 用于修饰成员的修饰符

独立于每个对象的,每个对象都要用到的设计成类成员。类变量经常用来计数,作初始状态。

类的所有实例共享类变量,每一个实例改变了类变量,都会永久地改变类变量,影响到其他的对象。

类方法中不能使用this和super;不能创建内部类的实例;不能直接引用实例变量;不能直接调用实例方法,必须创建实例对象,用对象引用或调用。

当成员被静态修饰后,除了可以被对象调用,还可以直接被类名调用 类名.静态成员

特点:  随类的加载而加载,消失而消失,生命周期最长;优先于对象存在;可以直接被类名所调用;被所有对象所共享

 

实例变量与类变量的区别:

①声明时  有无static

②引用方式  实例变量必须通过对象引用,类变量一般通过类引用

③存放位置

  类变量随着类的加载而存在方法区中,实例变量随着对象的建立存在堆内存中

④生命周期

类变量随类的消失而消失,生命周期最长

实例变量随对象的消失而消失

 

静态的利弊

利:对对象的共享数据进行单独空间的存储,可以直接被类名调用

弊:生命周期过长,访问出现局限

 

静态使用注意事项:

1)静态方法只能直接访问静态成员,若要访问非静态成员,需生成对象,用对象调用;非静态方法可以访问静态成员

2)静态方法中不能定义this super关键字,静态优先于对象存在

3)主函数是静态的

 

main函数

public static void main(String[] args)

作为程序的入口,可以被JVM调用

public 访问权限最大

static 主函数随着类的加载已经存在

void 无具体返回值

main 不是关键字,可以被jvm识别

(String[] args) 参数,字符串类型的数组,jvm在调用主函数时,传入的是new string[0]

 

当对象中出现共享数据时,使用静态变量;当功能内部没有访问到非静态数据时,使用静态函数。

 

静态应用:工具类

将ArrayTool中的方法都定义成static的,直接通过类名调用。

 

静态代码块    static{   }

给类进行初始化  随类的加载而执行,只执行一次。

执行顺序:静态代码块  构造代码块 构造函数

 

对象的初始化过程

Person p=new Person("zhang",20);

1)找到person.class文件并加载到内存中

2)执行该类中的static代码块,给person类初始化

3)在堆内存中开辟内存空间,分配内存地址

4)在堆内存中建立对象的特有属性,并进行默认初始化

5)对属性进行显示初始化

6)对对象进行构造代码块初始化

7)对对象进行构造函数初始化

8)将内存地址赋给栈内存中的p变量

 

对象被创建的过程

①寻找类定义

②加载类定义

③给对象分配内存空间

④对象的初始化顺序

  给父类静态变量默认值;对父类静态变量赋值;执行父类静态块;

   给当前类静态变量默认值;对当前类静态变量赋值;执行当前类静态块;

   给父类变量默认值;对父类变量赋值;执行父类构造函数;

   给当前类变量默认值;对当前类变量赋值;执行当前类构造函数

⑤对象构造完成

先父类后子类,先静态后实例,先变量后构造方法,先默认值后赋初值。

 

方法调用的优先顺序:

优先级由高到低 this.show(obj),super.show(obj),this.show((super)obj),super.show((super)obj)

帮助文档的制作

/**       */

该类必须用public修饰

javadoc -d myhelp -auther -version ArrayTool.java

 

单例设计模式

 解决一个类在内存中只存在一个对象

饿汉式 -singke类一进内存就进行初始化

Class Single{

  private Single(){};

  private static Single s=new Single();

  public static Single getInstance()

    {   return s;  }

 }

将构造函数私有化 ->避免其他程序过多的建立该对象

在类中创建一个本类对象 ->为了让其他程序可以访问到该类对象

提供一个方法可以访问到该对象 ->方便其他程序对自定义对象的访问

懒汉式

对象的延时加载

Single类进内存,对象没有存在,只有调用了getInstance方法才建立对象

Class Single{

  private Single(){};

  private static Single s=null;

  public static Single getInstance()

    {  if(s==null)

           s=new Single();

       return s;  

    }

 }

 

抽象 abstract 修饰类  函数

当多个类中出现相同功能 但是功能主体不同 这是可以进行向上抽取。只抽取功能定义,而不抽取功能主体。

抽象类

抽象方法一定在抽象类中;抽象类和抽象方法都必须被abstract修饰;抽象类不可以用new创建对象,调用抽象方法没意义;抽象类中的方法要被使用,必须由子类复写其所有抽象方法后,建立子类对象调用,若只覆盖了部分抽象方法,则该之类还是抽象类。

抽象类比一般类多了抽象函数,不可以实例化。抽象类可以不定义抽象方法,是为了不让该类建立对象。

抽象方法只需要给出方法声明,不需要给出方法体。不能将构造方法,类成员方法声明为抽象方法;abstract抽象方法不能和static同用;父类的非抽象方法可以被子类的抽象方法覆盖。

 

 

 

接口   interface

提供了方法声明与方法实现相分离的机制

具备三个特征:公共性,静态的和最终的

声明接口的语法格式:

[public] interface 接口 [extends 父接口]  {

   [ public ] [static] [ final ] 数据类型  成员变量=常量值;

   [public] [ abstract] 返回值类型  成员方法 [(参数列表)];

}

不可以创建对象;需要被子类实现implements,子类对接口中的抽象方法全部覆盖后,子类才可以实例化,否则子类是一个抽象类。

接口可以被类多实现,接口之间存在多继承。

接口的作用:多继承;解耦

对外暴露的规则;程序的功能拓展;降低耦合性

接口的特点:

①接口中的方法默认是public和abstract的(可省略),但类在实现接口方法时一定要用public修饰,不能用protected,private,final和static修饰接口中的实例成员方法。

②接口中所有抽象方法必须全部被实现接口的类或其子类覆盖。

③接口中的成员变量都是常量

④接口不能被实例化

⑤接口是引用数据类型

⑥接口嵌入类中,可以使用private修饰

⑦嵌入接口中的接口,一定要为public

⑧public接口可以被任何一个类使用,不加public,为友好接口,可被同包中的类使用。

基本功能定义在类中,拓展功能定义在接口中。

 

接口和抽象类

相同点:

都包含抽象方法;都不能被实例化;都是引用数据类型

不同点:

①抽象类约定多个子类之间共同使用的方法;接口中约定多个互不相关类之间共同使用的方法

②抽象类与子类之间采用单重继承;一个类实现多个接口则实现了多重继承

③抽象类及其类中成员具有与普通类一样的访问权限;借口的访问权限只有public和默认,接口中成员的访问权限都是public

④抽象类中可以包含非抽象方法,也可以声明构造方法;接口中的方法全部是抽象方法,不能声明构造方法

⑤抽象类中可以声明成员变量,子类可以对该成员变量赋值;接口中只能声明常量

⑥接口是指这个东西"能做什么";类的意思是这个东西"是什么"。可以用"能做什么"来描述"是什么",类的实例可以隐式转换为接口被使用。

 

6种变量类型:

类变量,实例变量,方法参数,构造函数参数,异常处理参数和局部变量

类变量包括在类中定义的静态数据成员以及在接口中声明的静态或非静态的数据成员。

 

内部类

含两种情形:属于类中成员;在方法中定义一个类

所处环境:可以是其他类的成员;可以在一个语句块的内部定义;可以在表达式的内部匿名定义

内部类可以直接访问外部类中成员,包括私有的;外部类要访问内部类,必须要建立内部类对象Outer.Inner in =new Outer().new Inner();

内部类可以被私有修饰

内部类中持有一个外部类的引用,格式为:外部类名.this.变量名,所以可以直接访问外部类 

访问格式:外部类名.内部类名  变量名=外部类对象.内部类对象    当内部类定义在外部类成员位置上时,而且非私有,可以在外部其他类中,直接建立内部类对象。

当内部类在成员位置上时,可以被成员修饰符修饰

private 将内部类在外部类中进行封装

static  当内部类被static修饰后,只能访问外部类中的static成员,出现了访问局限

外部其他类中  直接访问内部类中的非静态成员  new Outer.Inner().function();  访问静态成员  Outer.Inner.function();

注:当内部类中定义了静态成员,该内部类必须为静态,当外部类中的静态方法访问内部类时,内部类也必须为静态。

内部类定义在局部时,不可以被成员修饰符修饰,可以直接访问外部类中的成员,因为还持有外部类中的引用,但是不可以访问它所在的局部中的变量,只能访问被final修饰的局部变量

匿名内部类:   

内部类的简写格式(其实是一个匿名子类对象,带内容的对象)

定义前提:内部类必须是继承一个类或者实现一个接口

格式: new 父类或者接口() {定义子类内容}  其定义的方法最好不要超过三个

必须实现父类以及接口中的所有抽象方法;总是使用父类的无参构造方法来创建实例

匿名类没有名字,不能用来声明对象引用,但可以显式的调用一个无参的父类(或接口)的构造方法,直接创建一个对象。

匿名类不能声明static成员变量和方法。

匿名类的主要用途是向方法的参数传值。

 

内部类的类特性:

不能与外部类同名;可以有各种访问权限,约定和外部类完全一样;具有封装性,继承性,抽象性;可以声明内部类为接口,但必须被其他内部类实现;只有静态内部类能嵌入接口中

内部类的成员特性:

实用点操作符','引用内部类;内部类具有类成员的4种访问权限

实例成员内部类:

①外部类和实例成员内部类可以互相访问对方的私有成员及其他成员,内部类还可以访问外部类的静态成员

②在外部类的实体内可以任意创建内部类对象

③在内部类中可以直接引用其外部类的所有域

④实例内部类中不能定义静态成员变量

⑤实例内部类中,如果访问自己定义的属性,可以直接访问,也可以通过this来访问

⑥若外部类和内部类定义的属性同名,可以通过"外部类类名.this.外部类成员变量名"来访问

⑦在外部类的静态方法之内以及外部类之外创建一个内部类对象,就要用 外部类类名.内部类类名 的形式制定该对象的类型

静态成员内部类:

①只能访问外部类的静态成员

②静态内部类变为顶层类,不能再使用局部变量

③想要在内部类中声明任何static成员,则该内部类必须声明为static

局部内部类(定义在方法中):

①局部内部类没有任何的访问控制权限,外部类看不见方法中的局部内部类,局部内部类不能从所在的方法外引用,但是局部内部类可以访问外部类中的任何成员。

②方法体中可以访问局部内部类,但是必须在定义局部内部类之后

③在局部内部类中,只能访问外部方法中用final修饰的局部变量

④局部内部类中不能定义静态属性

⑤内部类的实例化必须先实例化外部类

 

设计原则:

①面向抽象原则

抽象概念和具体实现相隔离

降低程序的耦合性

②开闭原则

 对拓展开放,对修改关闭

③少用继承多用组合原则

 

 

异常

程序在运行时出现的不正常情况

异常由来:问题也是现实生活中一个具体的事物,也可以通过java类的形式进行描述,并封装成对象。其实就是java对不正常情况进行描述后的对象体现。

Throwable类分为: Error类  严重的  一般不编写针对性的代码对其处理  Exception类  非严重的  使用针对性的方式进行处理

异常处理:

try{  需要被检测的代码 }

catch  (异常类  变量)

{  处理异常的代码  }

finally  {一定会执行的语句}

对捕获到的异常进行常见方法操作:

getMessage() 捕获异常信息

toString()  异常信息 异常名称

printStackTrace()  异常名称 信息 出现位置

(jvm默认的异常处理机制,是调用printStackTrace方法,打印异常的堆栈的跟踪信息,在功能上通过throws的关键字声明了该功能有可能会出现问题。便于提高安全性,让调用者进行处理,不处理编译失败

多异常处理

声明异常时,建议声明更为具体的异常,这样可以处理的更具体。

声明几个异常就对应几个catch块,不要定义多余的;如果多个catch块中的异常出现继承关系,父类catch块放在最下面

在进行catch处理时,catch中一定要定义具体的处理方式,不要简单定义一句e.printStackTrace()或只写一个输出语句

自定义异常

好处:按照java面向对象思想,将程序中出现的特有问题进行封装

当在函数内部出现了throw抛出异常对象,则必须要给对应的处理动作,要么在内部try catch,要么在函数上声明让调用者处理。一般情况下,函数内出现问题,函数上需要声明。

如何定义异常信息?

继承Exception或者RuntimeException(该异常发生,无法再继续进行运算时),具备可抛性和操作异常的共性方法

父类中已经把异常信息的操作都完成了,所以子类只要在构造时,将异常信息传递给父类通过super语句,则可以直接通过getMessage方法获取自定义异常信息

throw和throws的区别

throw使用在函数内,throws使用在函数上;

throws后面可以跟多个异常类,用逗号隔开 throw后面跟的是异常对象

RuntimeException  Exception中特殊的子类异常,运行时异常

如果在函数内抛出该异常,函数上可以不用声明,编译一样通过;如果在函数上声明了该异常,调用者可以不进行处理,编译可以通过。

当该异常发生时,希望程序停止,因为在运行时出现了无法继续运算的情况,希望停止程序后,对代码进行修正。

finally  存放一定会被执行的代码,通常用于关闭资源。 

return;  finally中的语句也会执行

 只有一种情况不执行: System.exit(0);  系统退出,jvm结束

异常处理格式:

try{} catch{}

try{} catch{}  finally{}

try{} finally{}

catch是用于处理异常,如果没有catch就代表异常没有被处理过,如果该异常是检测时异常,则必须声明。

异常在子父类覆盖中的体现:

子类在覆盖父类时,如果父类的方法抛出异常,那么子类的覆盖方法,只能抛出父类的异常或者该异常的子类;如果父类方法抛出多个异常,子类在覆盖父类方法时,只能抛出父类异常的子集;如果傅雷或者接口的方法中没有异常抛出,那么子类在覆盖父类时,也不可以抛出异常,如果子类方法发生了异常,就必须要进行try处理,绝对不能抛。

异常分为两种:

编译时被检测异常:该异常在编译时,如果没有被处理(没有抛也没有try),编译失败。该异常被标识,代表可以被处理。

运行时异常(编译时不检测):  在编译时不需要处理,编译器不检查。该异常发生时,建议不处理,让程序停止,需要对代码进行修正。

异常的好处:

将问题进行封装;讲正常流程代码和问题处理代码相分离,方便阅读

异常的处理原则:

处理方式有两种 try或者throws;调用到抛出异常的功能时 抛出几个就处理几个,一个try对应多个catch;

多个catch,父类的catch放到最下面;

catch内需要定义针对性的处理方式,不要简单的定义printStackTrace输出语句,也不要不写。如果捕获到的异常,本功能处理不了时,可以继续在catch中抛出;如果该异常处理不了,但并不属于该功能出现的异常,可以将异常转换后,再抛出和该功能相关的异常,或者异常可以处理,当需要将异常产生的和本功能相关的问题提供出去,让调用者知道并处理,也可以将捕获的异常处理后转换成新的异常。

 

包 package

对类文件进行分类管理;给类提供多层命名空间;写在程序文件的第一行;类名的全称是 包名.类名;包也是一种封装形式

包与包之间的访问,被访问的包中的类,以及类中成员需要被public修饰

不同包中的子类还可以直接访问父类中被protected权限修饰的成员

包与包之间可以访问的权限只有两种:public protected

import  导入包中的类  建议不要写通配符*,用哪个导哪个

定义包名不要重复,可以使用url来完成定义

                   public  protected default  private

同一个类中  √           √             √            √

同一个包中  √           √             √

子类              √           √

不同包中      √

 

多线程

进程是一个正在执行中的程序,每一个进程执行都有一个执行顺序,该顺序是一个执行路径,或者叫一个控制单元

线程就是进程中的一个独立的控制单元,线程是控制着进程的执行。

一个进程中至少有一个线程

JVM启动时就会有一个线程java.exe,该进程中至少有一个线程负责java程序的执行,而且这个线程运行的代码存在于main方法中,该线程称之为主线程。jvm启动不止有一个线程,还有负责垃圾回收机制的线程。

如何在自定义代码中,自定义一个线程?

继承Thread类

步骤:1) 定义类继承Thread类

        2)复写Thread类中的run方法(将自定义方法存储在run方法中,运行代码)

       3)调用线程的Start方法(启动线程,调用run方法)

运行结果每次都不同?因为多个线程都获取CPU的执行权,CPU执行到谁,谁就运行。在某一时刻只能有一个程序运行(多核除外),CPU在做着快速的切换,以达到看上去是同时运行的效果。多线程的运行行为形容为互相抢夺CPU的执行权,多线程的一个特性:随机性,谁抢到谁执行。

覆盖run方法的原因:Thread类用于描述线程,该类中定义了一个存储功能run方法,用于存储线程要运行的代码。

d.Start()开启线程并执行线程中的run方法

d.run()仅仅是对象调用方法,而线程创建了并没有运行

②实现Runnable接口

步骤:1)定义类实现Runnable接口

2)覆盖Runnable接口中的run方法

3)通过Thread类建立线程对象

4)将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数(自定义的run方法所属的类是Runnable接口的子类对象,要让线程去执行指定对象的run方法)

5)调用Thread类的start方法开启线程并调用Runnable接口子类的run方法

实现方式和继承方式的区别:

实现方式避免了单继承的局限性,建议使用。继承方式线程代码存放在Thtead子类run方法中;实现方式线程代码存在接口子类的run方法中。

线程运行状态:

                          阻塞  (具备运行资格,但没有执行权)

                         (临时状态)

                               ↑

                               ↓

被创建-Start()->运行  -Sleep(time)->冻结(放弃了执行资格)

                                       <-sleep时间到-

                                       -wait()->

                                      <-notify()-

                             ↓stop()

                          消亡

获取线程对象以及名称:

线程都有自己默认的名称  Thread-编号  从0开始

static Thread currentThread();获取当前线程对象

getName()  获取线程名称

Thread.currentThread().getName()  标准通用方式

this.getName()

设置线程名称:setName或者构造函数

 

多线程的安全问题:

问题原因:当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分还没有执行完,另一个线程参与进来执行,导致共享数据的错误。

解决办法:同步代码块

synchronized(对象)

{ 需要被同步代码块}

同步函数  在权限修饰符与返回值类型间加synchronized

函数需要被对象调用,函数都有一个所属对象引用this,同步函数的锁是this。

同步函数被静态修饰后,使用的锁是class对象,即该方法所在类的字节码文件               类名.class

同步的前提:必须要有两个或两个以上的线程;必须是多个线程使用同一个锁

必须保证同步中只能有一个线程在运行

好处:解决了多线程的安全问题

弊端:多个线程需要判断锁,较为消耗资源

多线程的单例设计模式 懒汉式

Class Single{

     private static Single s=null;

     private Single(){}

     public  static Single getInstance()

     {   if(s==null)

          {  synchronized(Single.class)

             {  if(s==null)

                s=new Single();

              }

           }

         return  s;

      }

  }

延时加载,多线程会出现安全问题。同步函数低效,双重判断解决了效率问题。

死锁   同步中嵌套同步

线程间的通信:

①等待唤醒机制

wait()  notify()  notifyAll()

都使用在同步中,因为要对持有监视器的线程操作,这些操作线程的方法定义在Object类中。这些方法在操作同步中的线程时,必须要标识他们所操作线程持有的锁,锁可以是任意对象,所以可以被任何对象调用的方法定义在Object类中,只有同一个锁上的被等待线程,可以被同一个锁上的notify唤醒。等待和唤醒必须是同一个锁。

JDK1.5提供了多线程升级解决方案

将同步synchronized换成了现实lock操作,将Object中的wait  notify  notifyAll换成了condition对象 该对象可以用lock锁进行获取 ,可以实现只唤醒对方操作。

释放锁的操作一定要执行,写在finally中。

停止线程:

stop方法已经过时

停止线程的方法只有一种,run方法结束。

开启多线程运行,运行代码通常是循环结构,只要控制住循环,就可以让run方法结束,停止线程。

特殊:当线程处于冻结状态,就不会读取到标记,线程就不会结束。

Thread 类提供了interrupt()方法,指定让冻结的线程恢复到运行状态来,对冻结进行清除,可以操作标记让线程结束。

守护线程:setDaemon

在启动线程前调用。当正在运行的线程都是守护线程时,jvm退出。

join方法  临时加入线程执行

 当A线程执行到了B线程的.join()方法时,A就会等待 等B线程都执行完,A才会执行。

优先级  默认是5

设置优先级:setPriority()

Thread.MAX_PRIORITY  最高优先级

 

String类

String s1="abc";

s1是一个类类型变量,"abc"是一个对象

字符串最大的特点:一旦被初始化就不可以被改变。String类复写了Object类中的equals方法,判断字符串是否相同

String s2=new String("abc");

String s3="abc";

s1在内存中有一个对象,s2有两个对象

s1==s2    false

s1.equals(s2)   true

s1==s3      true

字符串的常见操作:

获取

int  length()

char   charAt(int   index)获取位置上的某个字符

int  indexOf(int  ch)返回ch在字符串中第一次出现的位置,没有出现返回-1

int  indexOf(int ch,int fromIndex)

int   indexOf(String str

int  lastIndexOf(int  ch)

判断

boolean  contains(str)   是否包含某一子串

boolean   isEmpty()

boolean  startWith(str)  以...开头

boolean   endsWith(str)

boolean   equals(str)

boolean   equalsIgnoreCase(str) 忽略大小写

转换

将字符数组转成字符串

构造函数   String(char[])

          String(char[],offset,num)

静态方法  static  String  copyValueOf(char[])

       static  String  copyValueOf(char[],int offset, int num)

      static  String  valueOf(char[])

将字符串转成字符数组

   char[]  toCharArray()

字节数组转成字符串

      String(byte[])

      String(byte[],offset,num)

字符串转成字节数组

    byte[]  getBytes()

基本数据类型转成字符串

  static  String valueOf(int)

  static  String valueOf(double)

替换

  String replace(oldChar,newChar)

切割

  String[] split(regex)

子串 获取字符串中一部分

   String  substring(begin)   到结尾

   String  substring(begin,end)  包含头不包含尾

转换

   String toUpperCase()

   String  toLowerCase()

去空格

   String trim()

比较   按自然顺序

   int compareTo(String)  

 

StringBuffer

字符串缓冲区,是一个容器 长度可变化;直接操作多个数据类型;最终会通过toString方法变成字符串

C create  U update   R read  D delete

存储

  StringBuffer  append()  添加到已有数据的结尾处

  StringBuffer  insert(index ,数据)

删除

  StringBuffer  delete(start,end) 包含头不包含尾

  StringBuffer  deleteCharAt(index) 删除指定位置

 sb.delete(0,sb.length())  清空缓冲区

获取

  int  length()

  char   charAt(int   index)获取位置上的某个字

  int   indexOf(String str)

  int  lastIndexOf(str)

  String subString(start,end)

修改

  StringBuffer replace(start,end,str)

  void setCharAt(index,ch)

反转

  StringBuffer reverse()

将缓冲区制定数据存储到指定字符数组中

  void getChars(int srcBegin,int srcEnd, char[] dst, int dstBegin

StringBuilder 线程不同步,JDK1.5之后建议使用

StringBuffer   线程同步

升级的三个因素: 提高效率,建化书写,提高安全性

 

基本数据类型对象包装类

Byte  Short  Integer  Long  Float  Double  Boolean  Character

最常见的作用:基本数据类型和字符串之间做转换

基本数据类型转成字符串

   基本数据类型.tostring(基本数据类型值)

   Integer.tostring(34)

字符串转成基本数据类型

   静态调用方式

    xxx  a=Xxx.parseXxx(String)

    int a=Integer.parseInt("123")

    double b=Double.parseDouble("12.34")

    对象调用方式

    Integer i=new Integer("123");

    int  num=i.intValue();

十进制转成其他进制

   toBinaryString()  二进制

   toHexString()  十六进制

   toOctalString()  八进制

其他进制转成十进制

   parseInt(str,radix进制)

JDK1.5版本之后的新特性

 Integer x=4;  自动装箱

 x=x+2;  自动拆箱 x.intValue()

 Integer m=128;

 Integer n=128;

 m==n  false

 Integer a=127;

 Integer b=127;

 a==b  true

 a b指向了同一个Integer对象

 当数值在byte范围内时,对于新特性,如果该数值已经存在,则不会再开辟新的空间。

 

集合类

集合是存储对象最常用的一种方式。集合长度是可变的,用于存储不同类型的对象。

集合与数组的不同:

数组中既可以存储基本数据类型,又可以存储对象,但长度是固定的

集合中只能存储对象,但长度是可变的

Collection

   List

     ArrayList

     LinkedList

     Vector

   Set

     HashSet

     TreeSet

每一个容器对数据的存储方式不同,这个存储方式称之为数据结构

List :元素是有序的,元素可以重复,该集合体系有索引

List特有方法:

增   add(index,element);

       addAll(index,Collection);

删   remove(index)

改   set(index,element)

查   get(index)

       subList(from,to)

       listInterator()

列表迭代器 ListIterator  是Iterator的子接口,在迭代时不可以通过集合对象的方法操作集合中的元素,因为会发生ConcurrentModificationException异常,所以在迭代器时,只能用迭代器的方法来操作数据,Iterator方法是有限的,只能对元素进行判断,取出和删除的操作,若想要添加 修改等,需要使用其子接口listIterator。该接口只能通过List集合的listIterator方法获取。

ArrayList 

底层是数组结构,特点:查询速度快,增删稍慢

添加add()  获取长度size()  删除remove() clear()  判空isEmpty()  判断是否存在 contains() 取交集retainAll()

add()方法的参数类型是Object,以便接收任意类型对象

集合中存储的都是对象的引用(地址)

迭代器(集合的取出元素的方式)

Iterator it=al.iterator(); 获取迭代器

it.hasNext     it.next()

LinkedList

底层是链表数据结构,特点:增删速度很快,查询稍慢

特有方法:  addFirst  addLast

        getFirst  getLast    获取但不删除

        removeFirst   removeLast    获取并删除

     如果集合中没有元素,会出现NoSuchElementException异常

JDK1.6中出现了替代方式

    offerFirst  offerLast

    peekFirst   peejLast   获取但不删除

    pollFirst    pollLast    获取并删除

  如果集合中没有元素,会返回null

Vector

底层是数组数据结构,线程同步,被ArrayList替代了

枚举,Vector特有的取出方式,与迭代相似

Enumeration en=v.elements();

en.hasMoreElements()   en.nextElement()

Set:元素是无序的,存入和取出的顺序不一定一致,元素不可以重复

Set集合的功能和Collection是一致的

HashSet

底层数据结构是哈希数,线程是非同步的

通过hashCode和equals两个方法来保证元素的唯一性。只有元素的hashCode值相同,才会判断equals是否为true;否则不会调用equals方法。对于判断元素是否存在,以及删除等操作,依赖的是元素的hashCode和equals方法。

TreeSet

底层数据结构是二叉树,可以对Set集合中的元素进行排序。

存储自定义对象时,要实现comparable接口,并复写compareTo(),判断主要条件,次要条件。

保证元素唯一性的依据,compareTo方法 return 0。

排序方式一 让元素自身具备比较性

元素实现comparable接口,覆盖compareTo方法

排序方式二 当元素自身不具备比较性时 或者元素具备的比较性不是我们所需要的 这是要让集合自身具备比较性。在集合初始化时,就有了比较方式。

比较器定义方法:  定义一个类,实现Comparator接口,覆盖compare方法

定义比较器,将比较器对象作为参数传递给TreeSet集合的构造函数

当两种排序都存在时,以比较器为主。

 

泛型

JDK1.5出现的新特性,用于解决安全问题,是一个安全机制

好处:将运行时期出现的问题ClassCastException转移到了编译时期,方便于程序员解决问题,让运行时期问题减少,安全;避免了强制转换的麻烦

泛型格式:通过<>来定义要操作的引用数据类型

当使用集合时,将集合中要存储的数据类型作为参数传递到<>中即可。

泛型类:

当类中要操作的引用数据类型不确定的时候,早期定义Object来扩展,现在定义泛型来扩展。

Class Utils<QQ>

{     private   QQ  q;

      public void  setObject(QQ  q)

      {     this.q=q;   }

      public  QQ  getObject()

      {   return   q;    }

 }

泛型方法:

为了让不同方法可以操作不同类型,而且类型还不确定,则将泛型定义在方法上。泛型类定义的泛型,在整个类中有效,如果被方法使用,那么泛型类的对象明确要操作的具体类型后 ,所要操作的类型就已经确定了。

静态方法不可以访问类上定义的泛型。如果静态方法操作的引用数据类型不确定,可以将泛型定义在方法上。

public  static<> void meathed (W w)

泛型定义在接口上:

   interface  Inter<T> {}

    Class   Outer   implents  Inter<T> { }

泛型限定:

<?>   ?通配符,也可以理解为占位符

<?extends E>  可以接收El类型或者E的子类型  上限

<?super  E>  E类型或者E的父类型   下限

 

Map集合

存储键值对,一对一的往里存,而且要保证键的唯一性

添加

put(key,value)  相同的键,后添加的值会覆盖原有键对应值,并返回被覆盖的值

putAll(Map<? extends k,? extends v>,m)

删除

clear()    remove(key)

判断

containValue(value)

containKey(key)

isEmpty()

获取

get(key)      size()   values()获取Map集合中的所有值

entrySet()   keySet()

 

HashTable

底层是哈希表数据结构,不可以存入null键null值,线程同步,JDK1.0 效率低

HashMap

底层是哈希表数据结构,允许使用null键null值,线程不同步,JDK1.2,效率高

TreeMap   

底层是二叉树数据结构,线程不同步,用于给Map集合中的键进行排序

与Set很像,Set底层就是使用了Map集合

map集合的两种取出方式:

①Set<k> keySet()

  将map中的所有键都存入Set集合,因为Set具备迭代器,可以通过迭代方式取出所有的键,再根据get方法,获取每一个键对应的值

Set<string> keySet=map.keySet();

Iterator<String> it=keySet.iteratir();

while(it.hasNext)

{   String  key  =it.next();

    Steing  value=mao.get(key);

}

②Set<Map.Entry<k,v>>  entrySet

将集合中的映射关系存入到了Set集合中,而这个关系的数据类型就是Map.Entry

Map也是一个接口,它是Map接口的一个内部接口。

Set<Map.Entry<String,String>> entrySet=map.entrySet();

Iterator<Map.Entry<String,String>> it=entrySet.iterator();

while(it.hasNext())

{    <Map.Entry<String,String>> me=it.next();

     String  key=me.getKey();

     String   value=me.getValue();

}

 

集合框架的工具类 Collections

sort()  max()   binarySearch(list,key)二分法查找,找不到返回-(插入点)-1

fill(list,obj) 替换集合中的所有元素

replaceAll(list,oldVal,newVal)  替换

reverse(list)  反转

reverseOrder()  强行逆转指定比较器的顺序

SynList  

swap(list,i,j)  交换指定位置处元素

shuffle(list) 随机置换

 

用于操作数组的工具类  Arrays   

都是静态方法

①asList()  将数组变成list集合

  好处:可以使用集合的思想和方法来操作数组中元素

注:不可以使用集合的增删方法,数组的长度是固定的

②将集合变成数组  Collection接口中的toArray方法(限定对元素的操作,不需要增删)

指定类型的数组要定义的长度:创建一个刚好的数组最优,长度等于集合的size。

 

增强for 循环  (高级for循环)

 for(数据类型  变量名称 :要被遍历的对象array或collection)  {}

对集合进行遍历,只能获取元素,不能操作;迭代器除了遍历,还可以进行remove集合中元素,listIterator还可以在遍历过程中进行增删改查

传统for与高级for的区别  :高级for有一个局限,必须有被遍历的目标。

建议在遍历数组时使用传统for,可以定义脚标。

 

JDK1.5出现的新特性

①可变参数    int… arr

一种数组参数的简写形式,不用每次都手动建立数组对象,只要将要操作的元素作为参数传递即可,隐式将这些参数封装成数组

注:可变参数一定要定义在参数列表的最后面

②静态导入 static import

import static java.util.Arrays.*; 导入的是Array这个类中所有的静态成员

当类名重名时要指定具体的包名;当方法重名时 要指定具体所属的对象或者类

 

其他对象

①System

类中的方法和属性都是静态的

out  标准输出,默认是控制台; in  标准输入,默认是键盘

获取系统属性信息 Properties getProoerties(),Properties是Hashtable的子类,也就是Map集合的一个子类对象,可以通过Map的方法获取该集合中的元素,该集合中存储的都是字符串,没有泛型定义。

自定义特有信息 System.setProperty()

获取指定属性信息 System.getProperty()

获取所有属性信息

for(Object obj:prop.keySet())

{   String value=(String)prop.get(obj);

    System.out.println(obj+".."+value);

}

②Runtime

该类提供的获取本类对象的方法(单例设计模式)

 static Runtime getRuntime()

process cec(String) 在单独进程中执行指定字符串命令

③Date

按指定模式输出 

Date d=new Date();

SimpleDateFormat sdf=new SimpleDateFormat("yyyy年MM月dd日E hh:mm:ss");

sdf.format(d);格式化指定对象

④Calendar

设置日期 set(y,m,d)

加减日期 add(Calendar.Year,10)   -10

获取日期  get(Calendar.Year)

⑤Math-Random

ceil()  返回大于参数的最小整数

floor()  返回小于参数的最大整数

round()  四舍五入

pow(a,b)  a的b次幂

random()  >=0.0  <1.0

1~10

(int)Math.random()*10+1

  Random r=new Random();

  r.nextInt(10)+1;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值