从零说起: 1.Java基础与语法糖

本文深入探讨了Java编程的基础知识,包括语法特性如面向对象、跨平台和垃圾回收,运行机制,以及开发步骤。详细阐述了Java的数据类型、运算符、分支语句、循环结构、函数、枚举和常用类的用法。此外,还讲解了异常处理,如检查性异常和运行时异常,以及如何自定义异常。最后,介绍了String、StringBuffer、Integer等类的使用,并讨论了内存分区和内存管理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Java基础

​ 从几年前实习开始, 陆陆续续写了好多的markdown笔记, 早期的笔记大多是在B站和一些大牛博客分享而自己整理的笔记, 早期的笔记体系比较清晰,也是我的学习成长路线.

​ 后续的笔记也有一些在业界大佬的分享,官网论坛,github学习到的更深层次一点的东西以及一些工作的经验和踩到的坑, 以后我会把早期记下的笔记和目前在学习以及工作中遇到的整理成模块不定期分享出来,如果大家有不同的见解也希望能在评论区说出大家的看法.

​ 希望我们永远保持这份思考与热爱.

一.语法

1.特性

1.面向对象

2.跨平台 (需JVM)

3.健壮性 (基于C 和 C++ 的基础之上实现)

4.较高的安全性 (GC 垃圾自动回收装置,强制类型检查储存指定类型的数据,取消了指针)

2.运行机制

编译

执行

3.Java程序的开发步骤

1、为什么安装完jdk后不配置环境变量就能直接运行java,而不能运行javac

在安装jdk的时候jdk会自带一个jre(java运行环境),还会单独安装一个jre,默认路径是和jdk在同级目录,而且会将这个jre/bin/java.exe拷贝一份到C:\Windows\System32\目录中,而这个目录在系统安装的时候就被配置到了环境变量中,所以能运行java。

不能运行javac是因为javac.exe是位于%JAVA_HOME%\bin目录下面,这个可执行文件没有被配置到环境变量PATH中;

javac 可以将java源文件编译为class字节码文件

​ 如 javac HelloWorld.java

​ 运行javac命令后,如果成功编译没有错误的话,会出现一个HelloWorld.class的文件。

​ java 可以运行class字节码文件
​ 如 java HelloWorld

​ 注意java命令后面不要加.class

2、为什么配置CLASSPATH系统变量

CLASSPATH系统变量为类查找路径

1、在使用javac进行编译时遇到import时候就会通过这个变量里面配置的路径去查找。如果配置的是目录,则会查找目录下的.class或.java(.java会自动编译出.class)的类文件,如果是.jar文件,则会引用.jar中的类,也可以通过javac -cp 进行指定CLASSPATH

2、使用java运行java程序的时候import类的查找顺序为

jre/lib中的*.jar,或被-Xbootclasspath参数指定的路径中的.jar(由启动类加载器加载)

jre/lib/ext子孙目录中的.class和子文件中的*.jar文件,或者由java.ext.dirs系统变量指定的目录和文件(扩展类加载器加载)

CLASSPATH中配置目录的子孙目录中的.class和配置的.jar中的类(应用程序类加载器加载)

3、问什么CLASSPATH配置 “.;” 和;tools.jar

配置CLASSPATH的开头使用.;表示在使用java或javac优先从当前目录查找类资源;tools.jar 是系统用来编译一个类的时候用到的,jps,javap,jstat,jstack等Java工具也使用到了tools.jar中的一些api,其实使用javac命令进行编译的时候已经封装了tools.jar,所以可以不用配置到CLASSPATH中;

​ 1.由程序员编写程序源代码,即后缀名为 .java 的文件

​ 2.编译器编译

​ javac XXX.java , 成功后生成一个.class的文件

​ 3.由解释器去执行

​ 执行字节码文件 java XXX.???

4.java的两大核心机制

JVM : Java 虚拟机

GC : 垃圾回收器

过程: 硬件主机 --> 操作系统 --> JVM --> Java运行程序

5.常见名称

jdk: java开发工具包

jre: java运行环境

Java API : java应用程序编程接口

javac.exe : java编译器

java.exe : java运行时的解释器

6.java基本语法

1.命名

标识符在java语言中,凡是对类,变量,常量,等命名时,所使用的字符序列包括的内容:

​ 0-9, a-z, A-Z, $, _

注意规则:

​ 1.由数字字母下划线和美元符号组成

​ 2.不能以数字开头

命名对错
Demo3
3Demo

​ 3.区分大小写

​ 4.长度是无限制的

​ 5.不能是java的关键字或保留字

命名的规则:

​ 1.驼峰式命名

​ 2.见名知意

​ 3.长度尽量小于15位

2.变量

​ 概念: 计算机中的一块内存,用于储存数据并加以计算.

​ 使用: 变量声明,变量访问

变量的声明:

​ 变量的数据类型;

​ 变量的名称;

​ 变量的数值;

3.数据类型:

​ 分为两种数据类型: 基本数据类型 和引用数据类型.

计算机的单位:

​ bit (位) 2进制中 0 或 1.

​ byte(字节) 8个 0 或 1 组成.

基本数据类型: 4种8类

整数型
byte字节占 1 个字节8 bit (-128 – 127)
short短整型占 2 个字节16 bit (-2^15 – 2^15-1)
int整型占 4 个字节32 bit (-2^31 – 2^31-1)
long长整型占 8 个字节64 bit (-2^63 – 2^63-1)
浮点型
float单精度占 4 个字节
double双精度占 8 个字节
字符型
char字符型占 2 个字节0–65535 , 用 ‘’
布尔型
boolean布尔型占 1 个字节true/false

4.运算符

​ 1.算术运算符 : +,-,*,/,%,++,–

​ 2.赋值运算符 : =, +=,-=,*=,/=,%=

​ 3.关系运算符 : >,<,<=,>=,==,!= (都是boolean类型)

​ 4.逻辑运算符 : &,|,!,&&,||,^

​ 5.位运算符

​ 6.位移运算符

​ 7.三目运算符 : X ? Y : Z

5.分支语句

程序执行的三种结构:

​ 1.顺序结构

​ 2.选择结构

​ 3.分支结构

分支结构 :

​ 1.if( ){} else {}

​ 2.switch 语句 (jdk1.7之后可以写 String):

switch (变量 int/String){
    case常量一:
    	分支一;
    break;
    case常量二:
    	分支二;
    break;
    ...
    default:
    	其他方法;
}

​ default ,如字义,就是默认的意思,用在switch语法中,就是说如果没有在case 1/2/3/xxx范围内,则执行default,这个思路其实是为了保险,为了程序少出bug,应当有这样的好习惯。

​ default是最后一种可能,所以不用再加 break了。

​ 无论default在switch语句中的哪个位置,编译器都是先找case,程序从第1个找到的case开始判断执行,只有所有的case都不满足条件,才会执行default,(这一点说明编译器作者想的很多,兼容性很强),所以default的位置可以随便放,但是出于编码规范的考虑,一般写在最后

​ 注意:

​ 1.不只作用在 int 上,byte,short,char,long 类型都可以.

​ 2.case 顺序结构没有要求,且 case 后面的值唯一.

​ 3.default 语句可以选择添加或不添加

​ 4.break 防止switch穿透.

6.循环结构

​ 循环语句的四个组成部分:

​ 初始化部分: 对循环变量赋初值

​ 循环条件部分: 判断循环变量是否超出某个界限

​ 循环体部分: 要循环执行的具体逻辑

​ 迭代部分: 修改循环变量的值

​ 1.for循环

​ 2.while循环

​ while(表达式) {循环体};

​ 3.do-while 循环

​ do {循环体} while (循环条件判断);

​ 对比三个循环:

​ for循环和while循环都是先判断在执行,条件不满足,循环体一次都不执行,

​ do-while循环先执行后判断,至少执行一次

​ 循环的控制语句:

​ break:用于结束循环,表示彻底结束了循环,终止

​ continue: 用于循环中,表示只结束某一次循环,后面循环继续

7.函数

1.什么是函数

​ 类种定义特定功能的小程序.

​ main(): 主函数–保证类的独立运行

2.函数的格式

[修饰符一 修饰符二 ...] 返回值类型 方法名([形式参数列表]){
	程序代码;
	[return 返回值;]
}

​ 函数最上面不能写return语句,编译会报错:无法访问的语句

3.方法的重载

​ 在一个类中可以有相同的名字,参数列表不同的多个方法,调用时根据不同的参数列表选择对应的调用方法.

​ 参数列表: 参数的类型,顺序,个数.

​ 满足三点要求,则认为是方法的重载:

​ 1.发生在同一个类中.

​ 2.方法名相同.

​ 3.参数列表不同.

4.修饰符–访问的权限

​ 表示类,属性,方法的可见度

​ public: 公共的,都可以访问,没有限制;

​ protected: 受保护的,本类本包,不同包限于子类使用

​ default: 默认的,本类本包

​ private: 私有的, 本类中可以访问

8.Java内存分区

1.栈内存

​ 正在运行的方法储存在栈内存

2.堆内存

​ 用于存对象(有地址)的栈内存,根据地址查找对象

3.方法区

​ 类被类的加载器加载之后,类的所有方法都会进入方法区

4.本地方法区:

​ 有native进行修饰的方法

5.寄存器:

​ 硬件

9.Java中的53个关键字(含2个保留字)

1).访问修饰符的关键字(共3个)

关键字意思备注,常用
public公有的可跨包,(默认选择)
protected受保护的当前包内可用,不同包限于子类使用
private私有的当前类可用

​ 2).定义类、接口、抽象类和实现接口、继承类的关键字、实例化对象(共6个)

关键字意思备注,常用
classpublic class A(){} 花括号里有已实现方法体,类名需要与文件名相同
interface接口public interface B(){} 花括号里有方法体,但没有实现,方法体句子后面是英文分号“:”结尾
abstract声明抽象public abstract class C(){} 介于类与接口中间,可以有也可以没有已经实现的方法体
implements实现用于类或接口实现接口public class A interface B(){}
extends继承用于类继承类 public class A extends D(){}
new创建新对象A a=new A(); A表示一个类

3).包的关键字(共2个)

关键字意思备注,常用
import引入包的关键字当使用某个包的一些类时,仅需类名 然后使用ctrl+shift+o或者选定类名(类或属性或方法)按住ctrl+单击 即可自动插入类所在的包。如:JFrame 快捷键之后自动加入import javax.swing.JFrame;
package定义包的关键字将所有有关的类放在一个包类以便查找修改等。如:package javake.flycat.draw002;

4).数据类型的关键字(共12个)

关键字意思备注,常用
byte字节型8bit
char字符型16bit
boolean布尔型
short短整型16bit
int整型32bit
float浮点型32bit
long长整型64bit
double双精度64bit
void无返回public void A(){} 其他需要返回的经常与return连用
null空值
true
false

5).条件循环(流程控制)(共12个)

关键字意思备注,常用
if如果if(){} 如果小括号里面怎么怎么样 花括号就怎么怎么样
else否则,或者常与if连用,用法相同
while当什么的时候while 怎么样就do什么 while(){}
for满足三个条件时for ( ; ; ){}
switch开关switch(表达式) { case 常量表达式1:语句1; … case 常量表达式2:语句2; default:语句; } default就是如果没有符合的case就执行它,default并不是必须的. case后的语句可以不用大括号. switch语句的判断条件可以接受int,byte,char,short,不能接受其他类型.
case返回开关里的结果
default默认
do运行长与while连用
break跳出循环
continue继续中断本次循环,并开始下一次
return返回return 一个返回值类型
instanceof实例一个二元操作符,和==,>,<是同一类的。测试它左边的对象是否是它右边的类的实例,返回boolean类型的数据

6).修饰方法、类、属性和变量(共9个)

关键字意思备注,常用
static静态的属性和方法都可以用static修饰,直接使用类名.属性和方法名。 只有内部类可以使用static关键字修饰,调用直接使用类名.内部类类名进行调用。 static可以独立存在。静态块
final最终的不可被改变的方法和类都可以用final来修饰 final修饰的类是不能被继承的 final修饰的方法是不能被子类重写。常量的定义:final修饰的属性就是常量。
super调用父类的方法常见public void paint(Graphics g){super.paint(g); ··· }
this当前类的父类的对象调用当前类中的方法(表示调用这个方法的对象)this.addActionListener(al):等等
native本地https://www.cnblogs.com/sxhjoker/p/11349330.html
synchronized线程,同步
strictfp严格,精准strictfp, 即 strict float point (精确浮点)。让你的浮点运算更加精确,而且不会因为不同的硬件平台所执行的结果不一致的话,可以用关键字strictfp.
transient短暂Java的serialization提供了一种持久化对象实例的机制。当持久化对象时,可能有一个特殊的对象数据成员,我们不想用serialization机制来保存它。为了在一个特定对象的一个域上关闭serialization,可以在这个域前加上关键字transient。当一个对象被序列化的时候,transient型变量的值不包括在序列化的表示中,然而非transient型的变量是被包括进去的。
volatile易失https://www.cnblogs.com/zhengbin/p/5654805.html

​ 7).错误处理(共5个)

关键字意思备注,常用
catch处理异常1.try+catch 程序的流程是:运行到try块中,如果有异常抛出,则转到catch块去处理。然后执行catch块后面的语句 2.try+catch+finally 程序的流程是:运行到try块中,如果有异常抛出,则转到catch块,catch块执行完毕后,执行finally块的代码,再执行finally块后面的代码。 如果没有异常抛出,执行完try块,也要去执行finally块的代码。然后执行finally块后面的语句 3.try+finally 程序的流程是:运行到try块中,如果有异常抛出的话,程序转向执行finally块的代码。那么finally块后面的代码还会被执行吗?不会!因为你没有处理异常,所以遇到异常后,执行完finally后,方法就已抛出异常的方式退出了。 这种方式中要注意的是,由于你没有捕获异常,所以要在方法后面声明抛出异常(来自网上的资料)
try捕获异常
finally有没有异常都执行
throw抛出一个异常对象一些可以导致程序出问题的因素,比如书写错误,逻辑错误或者是api的应用错误等等. 为了防止程序的崩溃就要预先检测这些因素,所以java 使用了异常这个机制.在java中异常是靠 “抛出” 也就是英语的"throw" 来使用的,意思是如果发现到什么异常的时候就把错误信息 “抛出”
throws声明一个异常可能被抛出把异常交给他的上级管理,自己不进行异常处理

8).不知道是什么(共2个)

关键字意思备注,常用
enum枚举,列举型别
assert断言

Enum枚举

Enum:代表一组常用常量,可用来代表一类相同类型的常量值如:

性别:
	public enum SexEnum {
  		male, female;
	}

颜色:
	public enum Color {
  		RED, BLUE,GREEN,BLACK;
	}

枚举对象里面的值都必须是唯一的。

throw 和throws的差别

区别一:

​ throw 是语句抛出一个异常;throws 是方法抛出一个异常;

​ throw语法:throw <异常对象>

​ 在方法声明中,添加throws子句表示该方法将抛出异常。

​ throws语法:[<修饰符>]<返回值类型><方法名>([<参数列表>])[throws<异常类>]

​ 其中:异常类可以声明多个,用逗号分割。

区别二:

​ throws可以单独使用,但throw不能;

区别三:

​ throw要么和try-catch-finally语句配套使用,要么与throws配套使用。但throws可以单独使用,然后再由处理异常的方法捕获。

throws E1,E2,E3 只是告诉程序这个方法可能会抛出这些个异常,方法的调用者可能要处理这些异常。而这些异常E1,E2,E3可能是该函数体产生的。

而throw是明确之处这个地方要抛出这个异常。

void doA() throws Exception1, Exception3 {

  try {
   	……
   	
  } catch(Exception1 e) {
   	throw e;
   	
  } catch(Exception2 e) {
	System.out.println("出错了");

  }
  if (a != b)
   	throw new Exception3("自定义异常");

  }

代码块……中可能产生异常Exception1、Exception2和Exception3。

如果产生Exception1异常,则捕捉了之后抛出由该方法的调用者去做处理;

如果产生Exception2异常,则该方法自己做了处理(打印出了说出错了),所以该方法就不会再向外抛出Exception2异常了,void doA() throws Exception1,Excpetion3里面的Exception2也就不用写了;

而Exception3异常是该方法的某段逻辑出错,程序员自己作了处理在该段逻辑错误的情况下抛出异常Exception3,则调用者也需要处理。

throw语句用在方法体内,表示抛出异常,由方法体内的语句处理

throws语句用在方法声明后面,表示再抛出异常,由调用这个方法的上一级方法中的语句来处理

throws主要是声明这个方法会抛出这种类型的异常,使其他地方调用它时知道要捕获这个异常。

throw是具体向外抛异常的动作,所以它是抛出一个异常实例。

throws说明你有哪个可能,倾向

throw的话,那就是你把那个倾向变成真实的了

同时:

1)throws出现在方法函数头;而throw出现在函数体;

2)throws表示出现异常的一种可能性,并不一定会发生这些异常;throw则是抛出了异常,执行throw则一定抛出了某种异常;

3)两者都是消极处理异常的方式(这里的消极并不是说这种方式不好),只是抛出或者可能抛出异常,但是不会由函数去处理异常,真正的处理异常由函数的上层调用处理。

二.枚举

JDK1.5引入了新的类型——枚举。在 Java 中它虽然算个“小”功能,却给我的开发带来了“大”方便。

用法一:常量

在JDK1.5 之前,我们定义常量都是: public static final… 。现在好了,有了枚举,可以把相关的常量分组到一个枚举类型里,而且枚举提供了比常量更多的方法。

Java代码

public enum Color {  
  RED, GREEN, BLANK, YELLOW  
} 

用法二:switch

JDK1.6之前的switch语句只支持int,char,enum类型,使用枚举,能让我们的代码可读性更强。

Java代码

enum Signal {  
    GREEN, YELLOW, RED  
}  

public class TrafficLight {  
    Signal color = Signal.RED;  
    public void change() {  
        switch (color) {  
        case RED:  
            color = Signal.GREEN;  
            break;  
        case YELLOW:  
            color = Signal.RED;  
            break;  
        case GREEN:  
            color = Signal.YELLOW; 
            break;  
        }  
    }  
}  

用法三:向枚举中添加新方法

如果打算自定义自己的方法,那么必须在enum实例序列的最后添加一个分号。而且 Java 要求必须先定义 enum 实例。

Java代码

public enum Color {  
    RED("红色", 1), GREEN("绿色", 2), BLANK("白色", 3), YELLO("黄色", 4);
    
    // 成员变量  
    private String name;  
    private int index;  
    
    // 构造方法  
    private Color(String name, int index) {  
        this.name = name;  
        this.index = index;  
    }  
    
    // 普通方法  
    public static String getName(int index) {  
        for (Color c : Color.values()) {  
            if (c.getIndex() == index) {  
                return c.name;  
            }  
        }  
        return null;  
    }  
    
    // get set 方法  
    public String getName() {  
        return name;  
    }  
    
    public void setName(String name) {  
        this.name = name;  
    }  

    public int getIndex() {  
        return index;  
    }  

    public void setIndex(int index) {
        this.index = index;  
    }  
}  

用法四:覆盖枚举的方法

下面给出一个toString()方法覆盖的例子。

Java代码

public enum Color {  
    RED("红色", 1), GREEN("绿色", 2), BLANK("白色", 3), YELLO("黄色", 4);  

    // 成员变量  
    private String name;  
    private int index;  

    // 构造方法  
    private Color(String name, int index) {  
        this.name = name;  
        this.index = index;  
    }  

    //覆盖方法  
    @Override  
    public String toString() {  
        return this.index+"_"+this.name;  
    }  
}  

用法五:实现接口

所有的枚举都继承自java.lang.Enum类。由于Java 不支持多继承,所以枚举对象不能再继承其他类。

Java代码

public interface Behaviour {  
    void print();  
    String getInfo();  
}  

public enum Color implements Behaviour{  
    RED("红色", 1), GREEN("绿色", 2), BLANK("白色", 3), YELLO("黄色", 4);  

    // 成员变量  
    private String name;  
    private int index;  

    // 构造方法  
    private Color(String name, int index) {  
        this.name = name;  
        this.index = index;  
    }  

	//接口方法  
    @Override  
    public String getInfo() {  
        return this.name;  
    }  

    //接口方法  
    @Override  
    public void print() {  
        System.out.println(this.index+":"+this.name);  
    }  

}  

用法六:使用接口组织枚举

Java代码

public interface Food {  
    enum Coffee implements Food{  
        BLACK_COFFEE,DECAF_COFFEE,LATTE,CAPPUCCINO  
    }  

    enum Dessert implements Food{  
        FRUIT, CAKE, GELATO  
    }  
}  

    /**
     * 测试继承接口的枚举的使用
     */
    private static void testImplementsInterface() {
        for (Food.Dessert dessertEnum : Food.Dessert.values()) {
            System.out.print(dessertEnum + "  ");
        }
        System.out.println();

        //我这地方这么写,是因为我在自己测试的时候,把这个coffee单独到一个文件去实现那个food接口,而不是在那个接口的内部。
        for (CoffeeEnum coffee : CoffeeEnum.values()) {
            System.out.print(coffee + "  ");
        }
        System.out.println();

        //搞个实现接口,来组织枚举,简单讲,就是分类吧。如果大量使用枚举的话,这么干,在写代码的时候,就很方便调用啦。
        //还有就是个“多态”的功能吧,
        Food food = Food.Dessert.CAKE;
        System.out.println(food);
        food = CoffeeEnum.BLACK_COFFEE;
        System.out.println(food);
    }

img运行结果

用法七:关于枚举集合的使用

java.util.EnumSet和java.util.EnumMap是两个枚举集合。EnumSet保证集合中的元素不重复;EnumMap中的 key是enum类型,而value则可以是任意类型。关于这个两个集合的使用就不在这里赘述,可以参考JDK文档。

关于枚举的实现细节和原理请参考:

参考资料:《ThinkingInJava》第四版

http://softbeta.iteye.com/blog/1185573

三.常用类

1.Object 老祖宗

java.lang.Object

  1. 所有的类直接或者间接继承父类 Java认为所有的对象都具备一些基本的共性内容 这些内容可以不断的向上抽取 最终就抽取到了一个最顶层的类中(Object) 该类中定义的就是所有对象都具备的功能

  2. 具体方法:

boolean equals(Object obj): 用于比较两个对象是否相等 其实内部比较的就是两个对象地址

String toString(): 将对象变成字符串 默认返回的格式: 类名@哈希值 = getClass().getName() + '@' + Integer.toHexString(hashCode()) 为了对象对应的字符串内容有意义 可以通过复写 建立该类对象自己特有的字符串表现形式

Class getClass(): 获取任意对象运行时的所属字节码文件对象

int hashCode(): 返回该对象的哈希码值 支持此方法是为了提高哈希表的性能

通常equals, toString, hashCode在应用中都会被复写 建立具体对象的特有的内容

2.String 字符串类

java.lang.String

  1. java中用String类进行描述 对字符串进行了对象的封装 这样的好处是可以对字符串这种常见数据进行方便的操作 对象封装后 可以定义N多属性和行为

  2. 特点: 字符串一旦被初始化 就不可以被改变 存放在方法区中的常量池中

  3. 具体方法:

    1. 构造方法 将字节数组或者字符数组转成字符串
String s1 = new String(); //创建了一个空内容的字符串

String s2 = null; //s2没有任何对象指向 是一个null常量值

String s3 = ""; //s3指向一个具体的字符串对象 只不过这个字符串中没有内容

String s4 = new String("abc");

String s5 = "abc"; //一般用此写法

new String(char[]); //将字符数组转成字符串

new String(char[], offset, count); //将字符数组中的一部分转成字符串

​ 2) 一般方法

a. 获取

length(); //获取字符串的长度

char charAt(int index); //指定位置的字符

int indexOf(int ch); //返回指定字符第一次出现的字符串内的索引 如果不存在返回-1

int indexOf(int ch, int fromIndex); //返回指定字符第一次出现的字符串内的索引 如果不存在返回-1

int indexOf(String str); // 返回第一次找到的字符串角标 如果不存在返回-1

int indexOf(String str, int fromIndex);

int lastIndexOf(int ch);

int lastIndexOf(int ch, int fromIndex);

int lastIndexOf(String str);

int lastIndexOf(String str, int fromIndex);

String substring(int start); //从start位开始 到length()-1为止

String substring(int start, int end); //从start开始到end为止 //包含start位 不包含end位

substring(0, str.length()); //获取整串

b. 判断

boolean contains(String substring); //字符串中包含指定的字符串吗

boolean startsWith(string); //字符串是否以指定字符串开头啊

boolean endsWith(string); //字符串是否以指定字符串结尾啊

boolean equals(string); //覆盖了Object中的方法 判断字符串内容是否相同

boolean equalsIgnoreCase(string) ; //判断字符串内容是否相同 忽略大小写

c. 转换

//可以通过字符串中的静态方法 将字符数组转成字符串

static String copyValueOf(char[]);

static String copyValueOf(char[], int offset, int count);

static String valueOf(char[]);

static String valueOf(char[], int offset, int count);

//将基本数据类型或者对象转成字符串

static String valueOf(char);

static String valueOf(boolean);

static String valueOf(double);

static String valueOf(float);

static String valueOf(int);

static String valueOf(long);

static String valueOf(Object);

//将字符串转成大小写

String toLowerCase(); //转小写

String toUpperCase(); //转大写

//将字符串转成数组

char[] toCharArray(); //转成字符数组

byte[] getBytes(); //可以加入编码表 转成字节数组

//将字符串转成字符串数组 切割方法

String[] split(分割的规则-字符串);

//将字符串进行内容替换 注意: 修改后变成新字符串 并不是将原字符串直接修改

String replace(oldChar, newChar);

String replace(oldstring, newstring);

String concat(string); //对字符串进行追加

String trim(); //去除字符串两端的空格

int compareTo(); //如果参数字符串等于此字符串 则返回值 0 如果此字符串按字典顺序小于字符串参数 则返回一个小于 0 的值 如果此字符串按字典顺序大于字符串参数 则返回一个大于0 的值

3.StringBuffer 字符串缓冲区

java.lang.StringBuffer

  1. 构造一个其中不带字符的字符串缓冲区 初始容量为16个字符

  2. 特点:

    1. 可以对字符串内容进行修改

    2. 是一个容器

    3. 是可变长度的

    4. 缓冲区中可以存储任意类型的数据

    5. 最终需要变成字符串

  3. 具体方法:

new StringBuffer(String str); StringBuffer转String

//添加

StringBuffer append(data); 在缓冲区中追加数据 追加到尾部

StringBuffer insert(index, data); 在指定位置插入数据

//删除

StringBuffer delete(start, end); 删除从start至end-1范围的元素

StringBuffer deleteCharAt(index); 删除指定位置的元素

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

//修改

StringBuffer replace(start, end, string); 将start至end-1替换成string

void setCharAt(index, char); 替换指定位置的字符

void setLength(len); 将原字符串置为指定长度的字符串

//查找 (查不到返回-1)

int indexOf(string); 返回指定子字符串在此字符串中第一次出现处的索引

int indexOf(string,int fromIndex); 从指定位置开始查找字符串

int lastIndexOf(string); 返回指定子字符串在此字符串中最右边出现处的索引

int lastIndexOf(string, int fromIndex); 从指定的索引开始反向搜索

//获取子串

string substring(start); 返回start到结尾的子串

string substring(start, end); 返回start至end-1的子串

//反转

StringBuffer reverse(); 字符串反转

4.StringBuilder 字符串缓冲区

java.lang.StringBuilder

  1. JDK1.5出现StringBuiler 构造一个其中不带字符的字符串生成器 初始容量为 16 个字符 该类被设计用作StringBuffer的一个简易替换 用在字符串缓冲区被单个线程使用的时候(这种情况很普遍)

  2. 具体方法: 和StringBuffer一样

  3. StringBuffer 和 StringBuilder 的区别:

    StringBuffer线程安全

    StringBuilder线程不安全

    单线程操作 使用StringBuilder 效率高

    多线程操作 使用StringBuffer 安全

5.Integer int对象包装类

java.lang.Integer

  1. 数字格式的字符串转成基本数据类型的方法

    1. 将该字符串封装成了Integer对象 并调用对象的方法intValue();

    2. 使用Integer.parseInt(numstring); 不用建立对象 直接类名调用

  2. 将基本类型转成字符串

    1. Integer中的静态方法 String toString(int);

    2. int + “”;

  3. 将一个十进制整数转成其他进制

    1. 转成二进制: toBinaryString

    2. 转成八进制: toOctalString

    3. 转成十六进制: toHexString

    4. toString(int num, int radix);

  4. 将其他进制转换十进制: parseInt(string, radix); //将给定的数转成指定的基数进制

  5. 在jdk1.5版本后 对基本数据类型对象包装类进行升级 在升级中 使用基本数据类型对象包装类可以像使用基本数据类型一样 进行运算

Integer i = new Integer(4); //1.5版本之前的写法
Integer i = 4; //自动装箱 1.5版本后的写法
i = i + 5;
//i对象是不能直接和5相加的 其实底层先将i转成int类型 在和5相加 而转成int类型的操作是隐式的 自动拆箱: 拆箱的原理就是i.intValue(); i+5运算完是一个int整数 如何赋值给引用类型i呢? 其实有对结果进行装箱

Integer c = 127;
Integer d = 127;
System.out.println(c == d); //true
//在装箱时 如果数值在byte范围之内 那么数值相同 不会产生新的对象 也就是说多个数值相同的引用指向的是同一个对象

6.System 系统类

java.lang.System

属性和行为都是静态的

long currentTimeMillis(); //返回当前时间毫秒值
Properties getProperties(); //获取当前系统的属性信息
exit();  //退出虚拟机

Properties prop = System.getProperties(); //获取系统的属性信息 并将这些信息存储到Properties集合中
System.setProperty("myname","啊啊啊"); //给系统属性信息集添加具体的属性信息
String name = System.getProperty("myname"); //获取指定属性的信息

7.Runtime 运行时环境类

java.lang.Runtime

类中没有构造方法 不能创建对象

但是有非静态方法 说明该类中应该定义好了对象 并可以通过一个static方法获取这个对象 用这个对象来调用非静态方法 这个方法就是 static Runtime getRuntime(); (单例设计模式)

class  RuntimeDemo {
    public static void main(String[] args) throws Exception {
        Runtime r = Runtime.getRuntime();
        Process p = r.exec("notepad.exe SystemDemo.java"); //运行指定的程序
        Thread.sleep(4000);
        p.destroy(); //杀掉进程
    }
}

8.Math 数学运算工具类

java.util.Math

用于数学运算的工具类 属性和行为都是静态的 该类是final不允许继承

static double ceil(double a); //返回大于指定数值的最小整数

static double floor(double a); //返回小于指定数值的最大整数

static long round(double a); //四舍五入成整数

static double pow(double a, double b); //a的b次幂

static double random(); //返回0~1的伪随机数

9.Date 日期类

java.util.Date

日期类 月份从0-11(这就是老外 奇奇怪怪)

//日期对象转成毫秒值

Date d = new Date();

long time1 = d.getTime();

long time2 = System.currentTimeMillis(); //毫秒值

//毫秒值转成具体的日期

long time = 1322709921312l;

Date d = new Date();

d.setTime(time);

//将日期字符串转换成日期对象 使用的就是DateFormat方法中的 Date parse(String source);

public static void method() throws Exception {

    String str_time = "2011/10/25";

    DateFormat df = new SimpleDateFormat("yyyy/MM/dd"); //SimpleDateFormat作为可以指定用户自定义的格式来完成格式化

    Date d = df.parse(str_time);

}

//将日期对象转换成字符串的方式 DateFormat类中的format方法

//创建日期格式对象

DateFormat df = new SimpleDateFormat(); //该对象的建立内部会封装一个默认的日期格式 11-12-1 下午1:48

//如果想要自定义日期格式的话 可使用SimpleDateFormat的构造函数 将具体的格式作为参数传入到构造函数中 必须要参与格式对象文档

df = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");

//调用DateFormat中的format方法 对已有的日期对象进行格式化

String str_time = df.format(d);

10.Calendar 日历类

java.util. Calendar



public static void method(){
    Calendar c = Calendar.getInstance();
    System.out.println(c.get(Calendar.YEAR) + "年" + (c.get(Calendar.MONTH)+1) + "月"
    + getNum(c.get(Calendar.DAY_OF_MONTH)) + "日"
    + "星期" + getWeek(c.get(Calendar.DAY_OF_WEEK)));
}

public static String getNum(int num){
    return num > 9 ? num + "" : "0" + num;
}

public static String getWeek(int index){
    //查表法 建立数据的对应关系
    //最好 数据个数是确定的 而且有对应关系 如果对应关系的一方 是数字 而且可以作为角标 那么可以通过数组来作为表
    String[] weeks = {"","日","一","二","三","四","五","六"};
    return weeks[index];
} 

11.File 文件类

java.io.File;

  1. 将文件系统中的文件和文件夹封装成了对象 提供了更多的属性和行为可以对这些文件和文件夹进行操作 这些是流对象办不到的 因为流只操作数据

  2. 具体方法:

//创建

boolean createNewFile(); 在指定目录下创建文件 如果该文件已存在 则不创建 而对操作文件的输出流而言 输出流对象已建立 就会创建文件 如果文件已存在 会覆盖 除非续写

boolean mkdir(); 创建此抽象路径名指定的目录

boolean mkdirs(); 创建多级目录

//删除 注意: 在删除文件夹时 必须保证这个文件夹中没有任何内容 才可以将该文件夹用delete删除 window的删除动作 是从里往外删 java删除文件不走回收站 要慎用

boolean delete(); 删除此抽象路径名表示的文件或目录

void deleteOnExit(); 在虚拟机退出时删除

//获取

long length(); 获取文件大小

String getName(); 返回由此抽象路径名表示的文件或目录的名称

String getPath(); 将此抽象路径名转换为一个路径名字符串

String getAbsolutePath(); 返回此抽象路径名的绝对路径名字符串

String getParent(); 返回此抽象路径名父目录的抽象路径名 如果此路径名没有指定父目录 则返回 null

long lastModified(); 返回此抽象路径名表示的文件最后一次被修改的时间

File.pathSeparator; 返回当前系统默认的路径分隔符 windows默认为";"

File.Separator; 返回当前系统默认的目录分隔符 windows默认为"\"

//判断

boolean exists(); 判断文件或者文件夹是否存在

boolean isDirectory(); 测试此抽象路径名表示的文件是否是一个目录

boolean isFile(); 测试此抽象路径名表示的文件是否是一个标准文件

boolean isHidden(); 测试此抽象路径名指定的文件是否是一个隐藏文件

boolean isAbsolute(); 测试此抽象路径名是否为绝对路径名

//重命名

boolean renameTo(File dest); 可以实现移动的效果 剪切+重命名

12.Properties

一个可以将键值进行持久化存储的对象 Map --> Hashtable的子类

用于属性配置文件 键和值都是字符串类型

特点: 可以持久化存储数据, 键值都是字符串, 一般用于配置文件

load(); 将流中的数据加载进集合 原理其实就是将读取流和指定文件相关联 并读取一行数据 因为数据是规则的key=value 所以获取一行后 通过 = 对该行数据进行切割 左边就是键 右边就是值 将键 值存储到properties集合中

store(); 写入各个项后 刷新输出流

list(); 将集合的键值数据列出到指定的目的地

四. 异常处理

​ 异常是程序中的一些错误,但并不是所有的错误都是异常,并且错误有时候是可以避免的。

​ 比如说,你的代码少了一个分号,那么运行出来结果是提示是错误 java.lang.Error;如果你用System.out.println(11/0),那么你是因为你用0做了除数,会抛出 java.lang.ArithmeticException 的异常。

​ 异常发生的原因有很多,通常包含以下几大类:

  • 用户输入了非法数据。

  • 要打开的文件不存在。

  • 网络通信时连接中断,或者JVM内存溢出。

    这些异常有的是因为用户错误引起,有的是程序错误引起的,还有其它一些是因为物理错误引起的。

    要理解Java异常处理是如何工作的,你需要掌握以下三种类型的异常:

  • **检查性异常:**最具代表的检查性异常是用户错误或问题引起的异常,这是程序员无法预见的。例如要打开一个不存在文件时,一个异常就发生了,这些异常在编译时不能被简单地忽略。

  • 运行时异常: 运行时异常是可能被程序员避免的异常。与检查性异常相反,运行时异常可以在编译时被忽略。

  • 错误: 错误不是异常,而是脱离程序员控制的问题。错误在代码中通常被忽略。例如,当栈溢出时,一个错误就发生了,它们在编译也检查不到的。


1.Exception 类的层次

​ 所有的异常类是从 java.lang.Exception 类继承的子类。

​ Exception 类是 Throwable 类的子类。除了Exception类外,Throwable还有一个子类Error 。

​ Java 程序通常不捕获错误。错误一般发生在严重故障时,它们在Java程序处理的范畴之外。

​ Error 用来指示运行时环境发生的错误。

​ 例如,JVM 内存溢出。一般地,程序不会从错误中恢复。

​ 异常类有两个主要的子类:IOException 类和 RuntimeException 类。

img

​ 在 Java 内置类中(接下来会说明),有大部分常用检查性和非检查性异常。


2.Java 内置异常类

​ Java 语言定义了一些异常类在 java.lang 标准包中。

​ 标准运行时异常类的子类是最常见的异常类。由于 java.lang 包是默认加载到所有的 Java 程序的,所以大部分从运行时异常类继承而来的异常都可以直接使用。

​ Java 根据各个类库也定义了一些其他的异常,下面的表中列出了 Java 的非检查性异常。

异常描述
ArithmeticException当出现异常的运算条件时,抛出此异常。例如,一个整数"除以零"时,抛出此类的一个实例。
ArrayIndexOutOfBoundsException用非法索引访问数组时抛出的异常。如果索引为负或大于等于数组大小,则该索引为非法索引。
ArrayStoreException试图将错误类型的对象存储到一个对象数组时抛出的异常。
ClassCastException当试图将对象强制转换为不是实例的子类时,抛出该异常。
IllegalArgumentException抛出的异常表明向方法传递了一个不合法或不正确的参数。
IllegalMonitorStateException抛出的异常表明某一线程已经试图等待对象的监视器,或者试图通知其他正在等待对象的监视器而本身没有指定监视器的线程。
IllegalStateException在非法或不适当的时间调用方法时产生的信号。换句话说,即 Java 环境或 Java 应用程序没有处于请求操作所要求的适当状态下。
IllegalThreadStateException线程没有处于请求操作所要求的适当状态时抛出的异常。
IndexOutOfBoundsException指示某排序索引(例如对数组、字符串或向量的排序)超出范围时抛出。
NegativeArraySizeException如果应用程序试图创建大小为负的数组,则抛出该异常。
NullPointerException当应用程序试图在需要对象的地方使用 null 时,抛出该异常
NumberFormatException当应用程序试图将字符串转换成一种数值类型,但该字符串不能转换为适当格式时,抛出该异常。
SecurityException由安全管理器抛出的异常,指示存在安全侵犯。
StringIndexOutOfBoundsException此异常由 String 方法抛出,指示索引或者为负,或者超出字符串的大小。
UnsupportedOperationException当不支持请求的操作时,抛出该异常。

​ 下面的表中列出了 Java 定义在 java.lang 包中的检查性异常类。

异常描述
ClassNotFoundException应用程序试图加载类时,找不到相应的类,抛出该异常。
CloneNotSupportedException当调用 Object 类中的 clone 方法克隆对象,但该对象的类无法实现 Cloneable 接口时,抛出该异常。
IllegalAccessException拒绝访问一个类的时候,抛出该异常。
InstantiationException当试图使用 Class 类中的 newInstance 方法创建一个类的实例,而指定的类对象因为是一个接口或是一个抽象类而无法实例化时,抛出该异常。
InterruptedException一个线程被另一个线程中断,抛出该异常。
NoSuchFieldException请求的变量不存在
NoSuchMethodException请求的方法不存在

3.异常方法

​ 下面的列表是 Throwable 类的主要方法:

序号方法及说明
1public String getMessage() 返回关于发生的异常的详细信息。这个消息在Throwable 类的构造函数中初始化了。
2public Throwable getCause() 返回一个Throwable 对象代表异常原因。
3public String toString() 使用getMessage()的结果返回类的串级名字。
4public void printStackTrace() 打印toString()结果和栈层次到System.err,即错误输出流。
5public StackTraceElement [] getStackTrace() 返回一个包含堆栈层次的数组。下标为0的元素代表栈顶,最后一个元素代表方法调用堆栈的栈底。
6public Throwable fillInStackTrace() 用当前的调用栈层次填充Throwable 对象栈层次,添加到栈层次任何先前信息中。

4.捕获异常

​ 使用 try 和 catch 关键字可以捕获异常。try/catch 代码块放在异常可能发生的地方。

​ try/catch代码块中的代码称为保护代码,使用 try/catch 的语法如下:

    try{
       // 程序代码
    }catch(ExceptionName e1){
       //Catch 块
    }

​ Catch 语句包含要捕获异常类型的声明。当保护代码块中发生一个异常时,try 后面的 catch 块就会被检查。

​ 如果发生的异常包含在 catch 块中,异常会被传递到该 catch 块,这和传递一个参数到方法是一样。

实例

​ 下面的例子中声明有两个元素的一个数组,当代码试图访问数组的第三个元素的时候就会抛出一个异常。

​ ExcepTest.java 文件代码:

// 文件名 : ExcepTest.java
import java.io.*;
public class ExcepTest{
 
   public static void main(String args[]){
      try{
         int a[] = new int[2];
         System.out.println("Access element three :" + a[3]);
      }catch(ArrayIndexOutOfBoundsException e){
         System.out.println("Exception thrown  :" + e);
      }
      System.out.println("Out of the block");
   }
}

​ 以上代码编译运行输出结果如下:

Exception thrown  :java.lang.ArrayIndexOutOfBoundsException: 3
Out of the block

多重捕获块

​ 一个 try 代码块后面跟随多个 catch 代码块的情况就叫多重捕获。

​ 多重捕获块的语法如下所示:

try{
   // 程序代码
}catch(异常类型1 异常的变量名1){
  // 程序代码
}catch(异常类型2 异常的变量名2){
  // 程序代码
}catch(异常类型2 异常的变量名2){
  // 程序代码
}

​ 上面的代码段包含了 3 个 catch块。

​ 可以在 try 语句后面添加任意数量的 catch 块。

​ 如果保护代码中发生异常,异常被抛给第一个 catch 块。

​ 如果抛出异常的数据类型与 ExceptionType1 匹配,它在这里就会被捕获。

​ 如果不匹配,它会被传递给第二个 catch 块。

​ 如此,直到异常被捕获或者通过所有的 catch 块。

实例

​ 该实例展示了怎么使用多重 try/catch。

    try{
      	file = new FileInputStream(fileName);
      	x = (byte) file.read();
    }catch(IOException i){
      	i.printStackTrace();
      	return -1;
    }catch(FileNotFoundException f) //Not valid!{
      	f.printStackTrace();
     	return -1;
    }

5.throws/throw 关键字:

​ 如果一个方法没有捕获到一个检查性异常,那么该方法必须使用 throws 关键字来声明。throws 关键字放在方法签名的尾部。

​ 也可以使用 throw 关键字抛出一个异常,无论它是新实例化的还是刚捕获到的。

​ 下面方法的声明抛出一个 RemoteException 异常:

    import java.io.*;
    public class className{
        public void deposit(double amount) throws RemoteException{
            // Method implementation
            throw new RemoteException();
        }
        //Remainder of class definition
    }

​ 一个方法可以声明抛出多个异常,多个异常之间用逗号隔开。

​ 例如,下面的方法声明抛出 RemoteException 和 InsufficientFundsException:

    import java.io.*;
    public class className{
       public void withdraw(double amount) throws RemoteException,InsufficientFundsException{
           // Method implementation
       }
       //Remainder of class definition
    }

6.finally关键字

​ finally 关键字用来创建在 try 代码块后面执行的代码块。

​ 无论是否发生异常,finally 代码块中的代码总会被执行。

​ 在 finally 代码块中,可以运行清理类型等收尾善后性质的语句。

​ finally 代码块出现在 catch 代码块最后,语法如下:

    try{
      // 程序代码
    }catch(异常类型1 异常的变量名1){
      // 程序代码
    }catch(异常类型2 异常的变量名2){
      // 程序代码
    }finally{
      // 程序代码
    }

实例

​ ExcepTest.java 文件代码:

    public class ExcepTest{
      public static void main(String args[]){
        int a[] = new int[2];
        try{
           System.out.println("Access element three :" + a[3]);
        }catch(ArrayIndexOutOfBoundsException e){
           System.out.println("Exception thrown  :" + e);
        }
        finally{
           a[0] = 6;
           System.out.println("First element value: " +a[0]);
           System.out.println("The finally statement is executed");
        }
      }
    }

​ 以上实例编译运行结果如下:

    Exception thrown  :java.lang.ArrayIndexOutOfBoundsException: 3
    First element value: 6
    The finally statement is executed

注意下面事项:

  • catch 不能独立于 try 存在。
  • 在 try/catch 后面添加 finally 块并非强制性要求的。
  • try 代码后不能既没 catch 块也没 finally 块。
  • try, catch, finally 块之间不能添加任何代码。

return和finally: https://blog.youkuaiyun.com/weixin_41005006/article/details/80643681


7.声明自定义异常

​ 在 Java 中你可以自定义异常。编写自己的异常类时需要记住下面的几点。

  • 所有异常都必须是 Throwable 的子类。

  • 如果希望写一个检查性异常类,则需要继承 Exception 类。

  • 如果你想写一个运行时异常类,那么需要继承 RuntimeException 类。

可以像下面这样定义自己的异常类:

    class MyException extends Exception{
    }

​ 只继承Exception 类来创建的异常类是检查性异常类。

​ 下面的 InsufficientFundsException 类是用户定义的异常类,它继承自 Exception。

​ 一个异常类和其它任何类一样,包含有变量和方法。

实例

​ 以下实例是一个银行账户的模拟,通过银行卡的号码完成识别,可以进行存钱和取钱的操作。

​ InsufficientFundsException.java 文件代码:

    // 文件名InsufficientFundsException.java
    import java.io.*;
 
    //自定义异常类,继承Exception类
    public class InsufficientFundsException extends Exception{
    
      	//此处的amount用来储存当出现异常(取出钱多于余额时)所缺乏的钱
      	private double amount;
      	public InsufficientFundsException(double amount){
        	this.amount = amount;
      	} 

      	public double getAmount(){
        	return amount;
      	}
	}

​ 为了展示如何使用我们自定义的异常类,

​ 在下面的 CheckingAccount 类中包含一个 withdraw() 方法抛出一个 InsufficientFundsException 异常。

​ CheckingAccount.java 文件代码:

    // 文件名称 CheckingAccount.java
    import java.io.*;

    //此类模拟银行账户
    public class CheckingAccount{
        
      	//balance为余额,number为卡号
       	private double balance;
       	private int number;
        
       	public CheckingAccount(int number){
          	this.number = number;
       	}
      	//方法:存钱
       	public void deposit(double amount){
          	balance += amount;
       	}
      	//方法:取钱
       	public void withdraw(double amount) throws InsufficientFundsException {
          	if(amount <= balance){
             	balance -= amount;
          	}else{
             	double needs = amount - balance;
             	throw new InsufficientFundsException(needs);
          	}
       	}
      	//方法:返回余额
       	public double getBalance(){
          	return balance;
       	}
      	//方法:返回卡号
       	public int getNumber(){
          	return number;
       	}
    }

​ 下面的 BankDemo 程序示范了如何调用 CheckingAccount 类的 deposit() 和 withdraw() 方法。

​ BankDemo.java 文件代码:

    //文件名称 BankDemo.java
    public class BankDemo{
        
       public static void main(String [] args){
           
          CheckingAccount c = new CheckingAccount(101);
          System.out.println("Depositing $500...");
          c.deposit(500.00);
           
          try{
             System.out.println("\nWithdrawing $100...");
             c.withdraw(100.00);
             System.out.println("\nWithdrawing $600...");
             c.withdraw(600.00);
          }catch(InsufficientFundsException e){
             System.out.println("Sorry, but you are short $" + e.getAmount());
             e.printStackTrace();
          }
       }
    }

​ 编译上面三个文件,并运行程序 BankDemo,得到结果如下所示:

    Depositing $500...

    Withdrawing $100...

    Withdrawing $600...
    Sorry, but you are short $200.0
    InsufficientFundsException
            at CheckingAccount.withdraw(CheckingAccount.java:25)
            at BankDemo.main(BankDemo.java:13)

8.通用异常

​ 在Java中定义了两种类型的异常和错误。

  • **JVM(Java虚拟机)异常:**由 JVM 抛出的异常或错误。例如:NullPointerException 类, ArrayIndexOutOfBoundsException 类,ClassCastException 类。

  • **程序级异常:**由程序或者API程序抛出的异常。例如 IllegalArgumentException 类,IllegalStateException 类。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值