一、对象内存管理:
- 编译好的Java程序需要运行在JVM中。
- 程序,无论是代码还是数据,都需要存储在内存中。JVM为Java程序提供并管理所需要的内存空间。
- JVM内存分为“堆”、“栈”和“方法区”三个区域,分别用于存储不同的数据。

①堆内存:
- JVM在其内存空间开辟了一个称为“堆”的存储空间;
- 这部分空间用于存储使用使用new关键字所创建的对象。

- 成员变量的生命周期:
- 访问对象需要依靠引用变量。
- 当一个对象没有任何引用时,会被GC(垃圾回收器)进行回收,该对象中的所有成员变量也随之被回收。
- 成员变量的生命周期为:从对象在堆中创建开始到对象被GC回收结束。
②垃圾回收机制:
③栈:

package MyTest;
public class Test{
int a;
public static void main(String[] args) {
Test t = new Test();
t.show(2);
}
public void show(int b){
int c;
System.out.println(a);
System.out.println(b);
System.out.println(c);
}
}
④方法区:
- 方法区用于存放类的信息:
- Java程序运行时,首先会通过类装载器载入类文件的字节码信息,经过解析后将其装入方法区,==类的各种信息(包括方法)==都在方法区中存储。

- 方法只有一份:
- 当类的信息被加载到方法区时,除了类的类型信息之外,同时在类内的方法定义也被加载到方法区。
- 类在实例化对象时,多个对象会拥有各自在堆中的空间,但是所有实例化对象是共用在方法区中的一份方法定义的。
二、Debug调试:
功能 | Eclipse | Idea |
---|
单步调试,会进入到方法中。 | F5 | F7 | 单步调试,不会进入到方法中。 | F6 | F8 | 结束方法的调试,返回。 | F7 | shift+F8 | 直接跳到下一个断点,若无断点则调试结束。 | F8 | shift+F8 |

Variables窗口:用于观看相关变量的值。
Watchers窗口:用于添加相关的逻辑表达式,用于判断真假。
三、API文档:
①JDK API:
-
什么是JDK API?
- JDK中包含大量的API类库,所谓的API(应用程序编程接口)就是一些已写好、可供直接调用的功能(在java中这些功能以类的形式封装)。
- JDK API包含的类库功能强大,经常使用的有:字符串操作、集合操作、文件操作、输入输出操作、网络操作、多线程等。
-
JDK包结构:
②文件注释规范:
文档注释主要只在三个地方出现:类上面、方法上面、变量上面。
package MyTest;
public class Test {
public static String string = "Hello World!";
public static void main(String[] args) {
int a = 7;
int b = 8;
System.out.println(string);
Plus(a,b);
}
public static void Plus(int num1,int num2){
int num=-1;
num = num1 + num2;
if (num>=10){
System.out.println(num);
}
}
}
③导出项目的JavaDoc文档:

-
下一步  -encoding utf-8 -charset utf-8
-encoding UTF-8 -charset UTF-8 -windowtitle "你的文档在浏览器窗口标题栏显示的内容" -link http://docs.Oracle.com/javase/7/docs/api
两者的区别就在于,有没有和java官方的文档“联动起来”。


当你点击java语言中已存在的类时,他会跳转到官方文档!

四、字符串String类:
①String是不变对象:
- java.lang.String使用了final进行修饰,所以不能被继承。
- 字符串底层封装了字符数组以及针对字符数组的操作算法。
- 字符串一旦创建,对象就永运无法改变,但是字符串引用可以重新赋值。
- Java字符串在内存中采用了Unicode编码方式,任何一个字符对应着两个字节的定长编码。
public class StringDemo {
public static void main(String[] args) {
String str1 = "Hello World!";
String str2 = "Hello World!";
System.out.println("Java重用了这一个字符串直接量!true or false?");
System.out.println(str1==str2);
}
}
public class StringDemo {
public static void main(String[] args) {
String str1 = "Hello";
String str2 = "Hello";
str1 = str1+"!";
System.out.println(str1);
System.out.println(str2);
}
}
- 当使用new语句创建String对象时,无论内容是否相等,都会强制创建一个新的对象!
public class StringDemo {
public static void main(String[] args) {
String str1 = "Hello";
String str2 = "Hello";
String str3 = new String("Hello");
System.out.println(str1==str2);
System.out.println(str1==str3);
System.out.println("分割线:--------------");
System.out.println(str1.equals(str3));
}
}
public class StringDemo {
public static void main(String[] args) {
String str1 = "HelloWorld";
String str2 = "Hello" + "World";
System.out.println(str1==str2);
String s = "Hello";
String str3 = s + "World";
System.out.println(str1==str3);
}
}
②修改字符串的性能问题:
- 当字符串频繁进行修改时:
- 此时就会频繁的新建字符串,效率就会大幅度降低,内存使用不高效,性能降低,垃圾回收器来不及收!
public class StringTest {
public static void main(String[] args) {
String str = "a";
for (int i=0;i<100000000;i++)
str = str + "a";
System.out.println("Game Over!");
}
}
public class StringTest {
public static void main(String[] args) {
String str = "好好学习 ";
StringBuilder sb = new StringBuilder();
StringBuilder stringBuilder = new StringBuilder(str);
System.out.println(stringBuilder.append("天天向上!").toString());
System.out.println(stringBuilder.replace(8,9,"后").toString());
System.out.println(stringBuilder.delete(0,5).toString());
System.out.println(stringBuilder.insert(2,"向上、向下、向左、向右、向前、"));
System.out.println(stringBuilder.reverse());
}
}
public class StringTest {
public static void main(String[] args) {
StringBuilder stringBuilder = new StringBuilder("a");
for (int i=0;i<10000000;i++)
stringBuilder.append("a");
System.out.println("Game Over!");
}
}
③String类中的常用函数:
- indexof函数:
- 返回给定字符串位于当前字符串的位置(首位置),如果找不到则返回-1。
public class StringTest {
public static void main(String[] args) {
String str = "HelloWorld!";
System.out.println("Length:"+str.length());
System.out.println("位置是:"+str.indexOf("Hello"));
System.out.println("位置是:"+str.indexOf("WYW"));
System.out.println("位置是:"+str.indexOf("o"));
System.out.println("位置是:"+str.indexOf("o",5));
System.out.println("位置是:"+str.lastIndexOf("o"));
}
}
- substring函数:
- 截取指定范围内的字符串。
- 注意:Java API有一个特点——通常使用两个数字表示的范围都是 “含头不含尾” 的!
public class StringTest {
public static void main(String[] args) {
String str = "HelloWorld";
System.out.println("截取的字符串为:"+str.substring(0,5));
System.out.println("截取的字符串为:"+str.substring(5));
}
}
public class StringTest {
public static void main(String[] args) {
String str = " HelloWorld! ";
System.out.println("去掉之前的效果是:"+str);
System.out.println("去掉之后的效果是:"+str.trim());
}
}
public class StringTest {
public static void main(String[] args) {
String str = "HelloWorld";
System.out.println("第6个字符是:"+str.charAt(5));
}
}
public class StringTest {
public static void main(String[] args) {
String str = "HelloWorld";
if (str.startsWith("Hello"))
System.out.println("该字符串是以Hello开始的!");
if (str.endsWith("World"))
System.out.println("该字符串是以World结尾的!");
}
}
- toUpperCase函数和toLowerCase函数:
public class StringTest {
public static void main(String[] args) {
String str = "HelloWorld";
System.out.println(str.toUpperCase());
System.out.println(str.toLowerCase());
}
}
public class StringTest {
public static void main(String[] args) {
int a = 10;
double b = 3.14;
char c = 'c';
float d = 3.145f;
System.out.println(String.valueOf(a));
System.out.println(String.valueOf(b));
System.out.println(String .valueOf(c));
System.out.println(String.valueOf(d));
String str = 10+"";
System.out.println("更加简单的方法:"+str);
System.out.print("测试结果为:");
System.out.println(1+2);
System.out.print("测试结果为:");
System.out.println(1+"2");
}
}
五、正则表达式:
①正则表达式简介:
正则表达式 | 说明 |
---|
[abc] | a、b、c中的任意一个字符 | [^abc] | 除了a、b、c的任意一个字符 | [a-z] | a、b、c、……、z中的任意一个字符 | [a-zA-Z0-9] | a ~ z、A ~ Z、0 ~ 9中的任意一个字符 | [a-z &&[ ^ bc]] | a ~ z中除了b和c以外的任意一个字符,其中&&表示“与”的关系 |
正则表达式 | 说明 |
---|
· | 任意一个字符 | \d | 任意一个数字字符,相当于[0-9] | \w | 单词字符,相当于[a-zA-Z0-9] | \s | 空白字符,相当于[\t\n\x0B\f\r] | \D | 非数字字符 | \W | 非单词字符 | \S | 非空白字符 |
正则表达式 | 说明 |
---|
X? | 表示0个或1个X | X* | 表示0个或任意个X | X+ | 表示1个到任意个X | X{n} | 表示n个X | X{n,} | 表示n个到任意个X | X{n,m} | 表示n个到m个X |
-
分组“()”:
- ()圆括号表示分组,可以将一系列的正则表达式看作一个整体,分组时可以使用 “|” 表示 “或” 的关系。
- 例如:abcabcabc = (abc){3};将abc看做一个整体进行重复。
- 例如:匹配收集号码前面的区号:(\ +86|0086)?\s?\d{11},圆括号表示这里需要出现“+86”或者“0086”。
-
" ^ “和” $ ":
- 没有这两个边界字符时,正则表达式是部分匹配。
- 没有这两个边界字符时,正则表达式是部分匹配,部分字符串满足条件就行;单独的" ^ “只看开头是否匹配,单独的” $ "只看结尾是否匹配;当两个一起使用时,才是完全匹配。
- **例如:**匹配用户名规则——从头到尾连续8~10个单词字符
- \w{8,10}
- 如果使用这种写法,则“abcd1234_abcd”是可以验证通过的。
- ^\w{8,10}$
- 如果使用这种写法,需要待检验字符串需要完全匹配,此时“abcd1234_abcd”是无法验证通过的。
- 但是在Java中,就算你不写边界符号,也是会自动加上去的,所以Java默认是全匹配!
②String类中有关于正则表达式的方法:
public class StringTest {
public static void main(String[] args) {
String email = "2000114@WYW.com.cn";
String regex = "\\w+@\\w+(\\.[a-zA-z]+)+";
boolean b = email.matches(regex);
if (b)
System.out.println("这是一个邮箱地址!");
else
System.out.println("这不是一个邮箱地址!");
}
}
- split方法:
- 使用给定的正则表达式来拆分当前字符串,并且将拆分之后的内容以字符串数组的方法返回。
- String[ ] split(String regex)
public class StringTest {
public static void main(String[] args) {
String str = "WYW:007Hello123The456World78765867!";
String regex = "\\d+";
String[] array = str.split(regex);
System.out.println(Arrays.toString(array));
}
}
- 一个有意思的特性:当多次匹配到同一个正则表达式时,将会出现什么样的情况!
public class StringTest {
public static void main(String[] args) {
String str = "WYW:007Hello123The456World78765867";
String regex = "\\d";
String[] array = str.split(regex);
System.out.println(Arrays.toString(array));
}
}
- 会在中间出现"",一个空的字符串!而且如果在开始或者中间连续匹配时,就会出现空串;但是在末尾出现时,就不会出现空串!
public class StringTest {
public static void main(String[] args) {
String regex = "\\d";
String str = "534534WYW007Hello123The456World7876";
String[] array = str.split(regex);
System.out.println(Arrays.toString(array));
}
}
- replaceAll方法:
- 将当前字符串中符合正则表达式要求部分的内容转换为给定内容!
- String replaceAll(String regex,String str)
public class StringTest {
public static void main(String[] args) {
String str = "abc123def456ghi789jkl000mno";
String regex = "[0-9]+";
System.out.println("之前的模样:"+str);
System.out.println("现在的模样:"+str.replaceAll(regex,"¥"));
}
}
个人体会:这些知识只是对于正则表达式的基础知识,在了解这些基础知识之后,要是构建更加复杂的正则表达式,你可网上查询、或者借助一些工具进行构建;没有必要花太多的时间深度学习!
六、Object:
-
Java Object 类是所有类的父类,也就是说 Java 的所有类都继承了 Object,子类可以使用 Object 的所有方法。 -
Object 中比较常用的两个方法就是 toString方法和equal方法。 -
Java API提供的类,都对于toString方法和equal方法进行了妥善的重写,所以不用担心,直接使用就好了!(不信你看下面包装类Integer里面对于equal函数是直接使用的)所以只有你直接写的类才需要进行重写!
①重写toString方法:
public class StringTest {
public static void main(String[] args) {
Point p = new Point(1,2);
System.out.println(p.toString());
System.out.println(p);
}
}
public class Point {
private int x;
private int y;
public Point(int x,int y) {
this.x = x;
this.y = y;
}
@Override
public String toString() {
return "Point{" +"x=" + x + ", y=" + y + '}';
}
}
②重写equal方法:
public class StringTest {
public static void main(String[] args) {
Point point = new Point(1,2);
Point point1 = new Point(1,2);
System.out.println(point == point1);
System.out.println(point.equals(point1));
}
}
public class Point {
private int x;
private int y;
public Point(int x,int y) {
this.x = x;
this.y = y;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Point point = (Point) o;
return x == point.x && y == point.y;
}
}
七、包装类:
①包装类概述:
- 在进行类型转换的范畴内,有一种特殊的转换,需要将 int 这样的基本数据类型转换为对象;所有基本类型都有一个与之对应的类,即包装类。
- 基本数据类型是直接以值的形式进行存储。
包装类的出现是为了解决基本数据类型无法参与面向对象开发问题!
- 包装类是不可变类,在构造了包装类对象之后,不允许更改包装在其中的值;包装类是 final 类型的,不能定义他们的子类。
基本类型 | 包装类 | 父类 |
---|
int | Integer | java.lang.Number | long | Long | java.lang.Number | double | Double | java.lang.Number | short | Short | java.lang.Number | float | Float | java.lang.Number | byte | Byte | java.lang.Number | char | Character | java.lang.Object | boolean | Boolean | java.lang.Object |
②基本类型转换为包装类:
public class Test {
public static void main(String[] args) {
int data = 1;
Integer i1 = new Integer(data);
Integer i2 = new Integer(data);
System.out.println(i1==i2);
System.out.println(i1.equals(i2));
}
}
- 方式二:通过valueof方法进行建立,重用了对象,建议使用下面的方式转换包装类。
public class Test {
public static void main(String[] args) {
int data = 1;
Integer i1 = Integer.valueOf(data);
Integer i2 = Integer.valueOf(data);
System.out.println(i1==i2);
System.out.println(i1.equals(i2));
}
}
补充:
-
但是这种重用是有限制的,当数据的取值超过一定的范围时,就会新建一个对象。
- 譬如:当上述代码的 data 取值128时,对象就不会重用。
- 通过查看valueof方法的源代码,我们可以发现,自数据取值超过一定的范围,将会新建一个对象。
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
-
不是所有的包装类的valueof都有这种对象重用的优化,譬如Double包装类就没有! public static Double valueOf(double d) {
return new Double(d);
}
-
Java还是推荐使用valueof方法来转化包装类,用得好可以重用优化,再不济和新建一个对象一样,怎么看都是稳赚不赔的买卖。
③包装类转换为基本类型:
public class Test {
public static void main(String[] args) {
Integer data = Integer.valueOf(128);
int in = data.intValue();
double dou = data.doubleValue();
float flo = data.floatValue();
byte by = data.byteValue();
System.out.println(by);
}
}
④包装类中的属性:
介绍两个常见的两个属性 Max_Value 和 Min_Value 用于表示该包装类对应的基本类型的取值范围。
public class Test {
public static void main(String[] args) {
System.out.println(Integer.MAX_VALUE);
System.out.println(Integer.MIN_VALUE);
System.out.println(Long.MAX_VALUE);
System.out.println(Long.MIN_VALUE);
System.out.println(Double.MAX_VALUE);
System.out.println(Double.MIN_VALUE);
}
}
⑤包装类用于字符串到基本类型数据的转换:
包装类都提供了一个静态方法parseXXX(String str)可以将字符串解析为对应的基本类型数据,但是该字符串必须能正确的描述出对应基本类型,否则将会抛出异常!
public class Test {
public static void main(String[] args) {
String string = "111.1";
int data = Integer.parseInt(string);
System.out.println(data);
double data1 = Double.parseDouble(string);
System.out.println(data1);
}
}
⑥自动拆装箱:
JDK 1.5版本推出时推出了一个特性——自动拆装箱,该特性是编译器认可的(不是JVM),当我们在基本类型和其对应的引用类型之间相互赋值时,编译器会自动补全代码,在两者之间转换!
public class Test {
public static void main(String[] args) {
int data = new Integer(12);
Integer integer = 4;
}
}
八、File:
使用File可以:
import java.io.File;
public class TestForFile {
public static void main(String[] args) {
File file = new File("./demo.txt");
System.out.println("文件名:"+file.getName());
System.out.println("文件大小:"+file.length()+"bytes");
System.out.println("是否可写:"+file.canWrite());
System.out.println("是否可读:"+file.canRead());
System.out.println("是否隐藏:"+file.isHidden());
}
}
①file创建删除文件:
import java.io.File;
import java.io.IOException;
public class TestForFile {
public static void main(String[] args) throws IOException {
File file = new File("./demo.txt");
System.out.println("原来是否已经存在该文件:"+file.exists());
if (!file.exists()){
file.createNewFile();
System.out.println("文件已经创建!");
}else{
System.out.println("文件已经存在!");
}
}
}
import java.io.File;
public class TestForFile {
public static void main(String[] args){
File file = new File("./demo.txt");
System.out.println("是否存在该文件:"+file.exists());
if (file.exists()){
file.delete();
System.out.println("文件已经删除!");
}else{
System.out.println("文件不存在!");
}
System.out.println("是否存在该文件:"+file.exists());
}
}
②file创建删除目录:
mkdir函数创建目录的前提就是该目录上级目录存在,不存在的时候就要使用后面的 mkdirs 创建多级目录了.
import java.io.File;
public class TestForFile {
public static void main(String[] args) {
File dir = new File("./demo");
if (!dir.exists()) {
dir.mkdir();
System.out.println("目录已创建!");
} else {
System.out.println("目录已存在!");
}
}
}
import java.io.File;
public class TestForFile {
public static void main(String[] args){
File dir = new File("./A/B/test");
if (!dir.exists()){
dir.mkdirs();
System.out.println("目录创建完毕!");
}else{
System.out.println("目录已经存在!");
}
}
}
delete方法删除目录的前提条件就是待删除目录下没有东西(空目录);所以只能逐级删除各个目录。
import java.io.File;
public class TestForFile {
public static void main(String[] args){
File dir = new File("./A/B");
if (dir.exists())
{
dir.delete();
System.out.println("目录删除成功!");
}else{
System.out.println("当前目录不存在!");
}
}
}
-
获取目录下的所有子项: File[] listFiles()
import java.io.File;
public class TestForFile {
public static void main(String[] args){
File dir = new File(".");
if (dir.isDirectory()){
File[] subs = dir.listFiles();
if (subs != null) {
for (File file:subs
) {
System.out.println(file.getName());
}
}else{
System.out.println("当前目录下为空!");
}
}
}
}
import java.io.File;
public class TestForFile {
public static void main(String[] args){
File file = new File("./A");
Delete(file);
}
public static void Delete(File file){
if (file.isDirectory()){
File[] subs = file.listFiles();
for (int i=0;i<subs.length;i++){
File sub = subs[i];
Delete(sub);
}
}
file.delete();
}
}
九、FileFilter文件过滤器:
ListFiles提供了一个重载的方法,可以指定一个文件过滤器(FileFilter),然后将满足所给过滤器要求的子项返回。
import java.io.File;
import java.io.FileFilter;
import java.util.Objects;
public class TestForFile {
public static void main(String[] args) {
File file = new File(".");
MyFilter filter = new MyFilter();
File[] subs = file.listFiles(filter);
for (int i = 0; i< Objects.requireNonNull(subs).length; i++)
System.out.println(subs[i]);
}
}
class MyFilter implements FileFilter {
public boolean accept(File pathname) {
String name = pathname.getName();
System.out.println("正在过滤:"+name);
return name.startsWith(".");
}
}
- 通过观察可以发现,MyFilter类被定义了之后只是被调用了一次,造成了浪费;因此我们可以使用匿名内部类实现该功能。
import java.io.File;
import java.io.FileFilter;
import java.util.Objects;
public class TestForFile {
public static void main(String[] args) {
File file = new File(".");
FileFilter filter = new FileFilter(){
@Override
public boolean accept(File pathname) {
return pathname.getName().startsWith(".");
}
};
File[] subs = file.listFiles(filter);
for (int i = 0; i< Objects.requireNonNull(subs).length; i++)
System.out.println(subs[i]);
}
}
- 不使用中间变量进行过渡,直接将其定义为参数在函数中使用。
import java.io.File;
import java.io.FileFilter;
import java.util.Objects;
public class TestForFile {
public static void main(String[] args) {
File file = new File(".");
File[] subs = file.listFiles(new FileFilter() {
@Override
public boolean accept(File pathname) {
return pathname.getName().startsWith(".");
}
});
for (int i = 0; i< Objects.requireNonNull(subs).length; i++)
System.out.println(subs[i]);
}
}
- 使用lambda表达式(JDK8之后)来进一步精简匿名内部类的使用。
import java.io.File;
import java.io.FileFilter;
import java.util.Objects;
public class TestForFile {
public static void main(String[] args) {
File file = new File(".");
File[] subs = file.listFiles((pathname -> pathname.getName().startsWith(".")));
for (int i = 0; i< Objects.requireNonNull(subs).length; i++)
System.out.println(subs[i]);
}
}
十、RandomAccessFile读写文件数据:
①基本介绍:
②写字节操作:
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
public class TestForFile {
public static void main(String[] args) throws IOException {
RandomAccessFile randomAccessFile = new RandomAccessFile("test.txt","rw");
randomAccessFile.write(66);
randomAccessFile.write('1');
System.out.println("写入完毕!");
randomAccessFile.close();
}
}
③读字节操作:
int read()
import java.io.IOException;
import java.io.RandomAccessFile;
public class TestForFile {
public static void main(String[] args) throws IOException {
RandomAccessFile randomAccessFile = new RandomAccessFile("test.txt","r");
int data = -1;
do {
data = randomAccessFile.read();
System.out.println(data);
}while (data!=-1);
randomAccessFile.close();
}
}
import java.io.IOException;
import java.io.RandomAccessFile;
public class TestForFile {
public static void main(String[] args) throws IOException {
RandomAccessFile r1 = new RandomAccessFile("MyPicture.jpg","r");
RandomAccessFile r2 = new RandomAccessFile("MyPicture_copy.jpg","rw");
int data = -1;
while ((data = r1.read())!=-1){
r2.write(data);
}
System.out.println("over!");
r1.close();
r2.close();
}
}
如果希望提高读写效率,可以通过提高每一次实际读写的数据量,减少实际发生的读写操作来做到。(空间换时间)
-
随机读写——单字节读写。 -
块读写——一组字节读写。
④块读写操作:
RandomAccessFile提供的块读写操作的方法:
int read(byte[] data)
- 一次性读取给定的字节数组长度的字节量并存入到该数组中;返回值为实际读取到的字节量,若返回值为-1,表示本次读取是文件末尾(没有读取到任何字节)。
void write(byte[] data)
void write(byte[] data,int start,int len)
- 一次性写出给定字节数组中从 start 开始的连续 len 个字节。
import java.io.IOException;
import java.io.RandomAccessFile;
public class TestForFile {
public static void main(String[] args) throws IOException {
RandomAccessFile r1 = new RandomAccessFile("Blueming.mkv","r");
RandomAccessFile r2 = new RandomAccessFile("Blueming_copy.mkv","rw");
int len = -1;
byte[] data = new byte[1024*10];
while ((len = r1.read(data))!=-1){
r2.write(data,0,len);
}
System.out.println("game over!");
r1.close();
r2.close();
}
}
⑤写字符串操作:
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.charset.StandardCharsets;
public class TestForFile {
public static void main(String[] args) throws IOException {
RandomAccessFile randomAccessFile = new RandomAccessFile("test.txt","rw");
String string = "Hello World!";
byte[] data = string.getBytes(StandardCharsets.UTF_8);
randomAccessFile.write(data);
System.out.println("写入完毕!");
randomAccessFile.close();
}
}
关键就在于使用getBytes方法将字符串转换为二进制形式的数据。
⑥读字符串操作:
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.charset.StandardCharsets;
public class TestForFile {
public static void main(String[] args) throws IOException {
RandomAccessFile randomAccessFile = new RandomAccessFile("test.txt","r");
byte[] data = new byte[(int) randomAccessFile.length()];
randomAccessFile.read(data);
String string = new String(data,StandardCharsets.UTF_8);
System.out.println(string);
randomAccessFile.close();
}
}
⑦读写基本类型数据:
import java.io.IOException;
import java.io.RandomAccessFile;
public class TestForFile {
public static void main(String[] args) throws IOException {
RandomAccessFile randomAccessFile = new RandomAccessFile("test.txt","rw");
int max = Integer.MAX_VALUE;
randomAccessFile.write(max>>>24);
randomAccessFile.write(max>>>16);
randomAccessFile.write(max>>>8);
randomAccessFile.write(max);
System.out.println("写入完毕!");
randomAccessFile.close();
}
}
- RandomAccessFile 中提供了写入基本数据类型的相关方法,基本原理和上面的演示程序一样,都是利用移位操作,逐个字节将数据"送"进去。
public final void writeInt(int v) throws IOException {
write((v >>> 24) & 0xFF);
write((v >>> 16) & 0xFF);
write((v >>> 8) & 0xFF);
write((v >>> 0) & 0xFF);
}
其他基本类型数据也提供了对应的方法,writeLong、writeFloat……
import java.io.IOException;
import java.io.RandomAccessFile;
public class TestForFile {
public static void main(String[] args) throws IOException {
RandomAccessFile randomAccessFile = new RandomAccessFile("test.txt","rw");
randomAccessFile.writeInt(Integer.MAX_VALUE);
randomAccessFile.writeLong(123L);
randomAccessFile.writeDouble(123.123);
System.out.println("写入完毕!");
int data = randomAccessFile.read();
System.out.println(data);
randomAccessFile.close();
}
}
按照前面的原理分析,我们可以预测输出的值,应当为127,但是结果却是-1?这是因为RandomAccessFile是基于指针进行操作的。当我们完成写数据操作时,指针已经位于文件的末尾,所以最终才会返回-1。
public long getFilePointer()
import java.io.IOException;
import java.io.RandomAccessFile;
public class TestForFile {
public static void main(String[] args) throws IOException {
RandomAccessFile randomAccessFile = new RandomAccessFile("test.txt","rw");
System.out.println("指针位于:"+randomAccessFile.getFilePointer());
randomAccessFile.writeInt(Integer.MAX_VALUE);
System.out.println("指针位于:"+randomAccessFile.getFilePointer());
randomAccessFile.writeLong(123L);
System.out.println("指针位于:"+randomAccessFile.getFilePointer());
randomAccessFile.writeDouble(123.123);
System.out.println("指针位于:"+randomAccessFile.getFilePointer());
System.out.println("写入完毕!");
int data = randomAccessFile.read();
System.out.println(data);
randomAccessFile.close();
}
}
- 知道了指针的位置后,我们可以通过将指针重新置于文件开始位置,就可以逐个读取文件中的数据了。
public void seek(long pos)
import java.io.IOException;
import java.io.RandomAccessFile;
public class TestForFile {
public static void main(String[] args) throws IOException {
RandomAccessFile randomAccessFile = new RandomAccessFile("test.txt","rw");
randomAccessFile.writeInt(Integer.MAX_VALUE);
randomAccessFile.writeLong(123L);
randomAccessFile.writeDouble(123.123);
System.out.println("写入完毕!");
randomAccessFile.seek(0);
System.out.println(randomAccessFile.readInt());
System.out.println(randomAccessFile.readLong());
System.out.println(randomAccessFile.readDouble());
randomAccessFile.close();
}
}
十一、JavaIO:
①IS与OS:
- 基本介绍:
- java io(input、output)输入与输出,是我们程序与外界交换数据的方式。java提供了一种统一的标准方式与外界交换数据。java将流按照功能划分为读和写,并使用不同的方式来表示。
- 其中输入流(外界到程序的方向)用于读取数据,输出流用于写出数据。
- java将流划分为两大类:节点流与处理流。
- 节点流:也称为低级流,是实际链接程序与数据源的“管道”,负责实际搬运数据;读写一定是建立在节点流的基础上进行的。
- 处理流:也称为高级流,不能独立存在;必须链接在其他流上,目的是当数据流当前流时对这些数据做某些处理,这样可以简化我们对于数据的操作
- 在实际应用中,我们是链接若干高级流,并最终链接低级流,通过低级流读写数据,通过高级流对读写的数据进行某些加工处理,完成一个复杂的读写操作;这个过程称为——流链接。
按照流是否直接与特定的地方(如磁盘、内存、设备等)相连,分为节点流和处理流两类。
-
节点流与处理流:
- 节点流:可以从或向一个特定的地方(节点)读写数据。
- 处理流:是对一个已存在的流的连接和封装,通过所封装的流的功能调用实现数据读写。
- 处理流的构造方法总是要带一个其他流对象做参数。一个流对象经过其他流的多次包装,称为流的链接。
- 通常处理流也称为高级流或过滤流。
-
IS和OS常用方法:
②文件流:
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
public class TestForIO {
public static void main(String[] args) throws IOException {
FileOutputStream fileOutputStream = new FileOutputStream("test.txt");
String string = "你好吗!我很好!";
fileOutputStream.write(string.getBytes(StandardCharsets.UTF_8));
System.out.println("写入完毕!");
fileOutputStream.close();
}
}
-
FileOutputStream常用构造方法: FileOutputStream(String path)
FileOutputStream(File file)
- 重写模式:以上两种方式创建时,默认覆盖写操作,即:若创建时发现该文件已存在,会先将该文件所有数据清除;然后通过当前流写出的内容作为该文件数据。
-
另外两种构造方法: FileOutputStream(String path,boolean append)
FileOutputStream(File file,boolean append)
- 追加写模式,即:若指定的文件存在,文件上的数据全保留,该流写出的数据会被追加到文件原数据的后面。
-
文件输入流:
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
public class TestForIO {
public static void main(String[] args) throws IOException {
FileInputStream fileInputStream = new FileInputStream("test.txt");
byte[] data = new byte[200];
int len = fileInputStream.read(data);
System.out.println("实际读取了"+len+"个字节!");
String str = new String(data,0,len,StandardCharsets.UTF_8);
System.out.println(str);
fileInputStream.close();
}
}
使用起来和RandomAccessFile类似的!
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
public class TestForIO {
public static void main(String[] args) throws IOException {
FileInputStream fileInputStream = new FileInputStream("Travel.mp3");
FileOutputStream fileOutputStream = new FileOutputStream("Travel_copy.mp3");
int len = -1;
byte[] data = new byte[1024*10];
while((len=fileInputStream.read(data))!=-1){
fileOutputStream.write(data,0,len);
}
System.out.println("复制完毕!");
fileInputStream.close();
fileOutputStream.close();
}
}
使用起来和RandomAccessFile类似的!
③缓冲流:
缓冲流是一对高级流,功能是提高读写效率;链接之后,无论我们进行随机读写还是块读写,当经过缓冲流时都会转换为块读写操作。
java.io.BufferedInputStream;
java.io.BufferedOutputStream;
import java.io.*;
public class TestForIO {
public static void main(String[] args) throws IOException {
FileInputStream fileInputStream = new FileInputStream("Travel.mp3");
BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream);
FileOutputStream fileOutputStream = new FileOutputStream("Travel_copy2.mp3");
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream);
int len = -1;
long start = System.currentTimeMillis();
while ((len = bufferedInputStream.read())!=-1){
bufferedOutputStream.write(len);
}
long end = System.currentTimeMillis();
System.out.println("复制完毕所花的时间为:"+(end-start)+"ms");
bufferedInputStream.close();
bufferedOutputStream.close();
}
}
当使用缓冲流链接低级流之后,其余的操作就要围绕着缓冲流来进行了,当操作完毕之后,close方法也是针对着缓冲流进行的。
利用的块读写的原理,无论我们进行随机读写还是块读写,当经过缓冲流时都会转换为块读写操作。
- 在打开了 BufferedInputStream 和 BufferedOutputStream 的源代码之后可以发现,每个类中都有一个字节数组用于实现类似于块读写功能,并且其默认大小是 8192(8Kb)。
protected volatile byte[] buf;
-
缓冲流缓冲区介绍:
缓冲流的 write 方法并不是立即将数据写出,而是先将数据存入其内部的数组中(上面的buf),当数组装满时才会做一次真实写操作。(转换为块写操作)
import java.io.*;
import java.nio.charset.StandardCharsets;
public class TestForIO {
public static void main(String[] args) throws IOException {
FileOutputStream fileOutputStream = new FileOutputStream("test.txt");
String string = "你好吗!我很好!";
fileOutputStream.write(string.getBytes(StandardCharsets.UTF_8));
System.out.println("game over!");
}
}
当刻意把 close 方法注释掉,再执行代码;我们查看相关的文件可以发现,数据写进去了!
import java.io.*;
import java.nio.charset.StandardCharsets;
public class TestForIO {
public static void main(String[] args) throws IOException {
FileOutputStream fileOutputStream = new FileOutputStream("test.txt");
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream);
String string = "你好吗!我很好!";
bufferedOutputStream.write(string.getBytes(StandardCharsets.UTF_8));
System.out.println("game over!");
}
}
当借助缓冲流(高级流)实现上述操作后,打开相关文件发现数据并没有写进去,此时数据还位于缓冲区中,没有写入到对应文件内。
- 我们可以使用 flush 方法,强制将缓冲区中的数据清空,写入到对应文件中。
import java.io.*;
import java.nio.charset.StandardCharsets;
public class TestForIO {
public static void main(String[] args) throws IOException {
FileOutputStream fileOutputStream = new FileOutputStream("test.txt");
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream);
String string = "你好吗!我很好!";
bufferedOutputStream.write(string.getBytes(StandardCharsets.UTF_8));
System.out.println("game over!");
bufferedOutputStream.flush();
}
}
此时我们查看对应文件可以发现,数据已经写入了对应文件;我们打开 close 方法的源代码也可以发现其函数体中会调用 flush 方法。
try {
flush();
} catch (Throwable e) {
flushException = e;
throw e;
}
flush 方法的意义是强制将缓冲流已经缓存的数据一次性写出。这样做可以让写出的数据有即时性,但是频繁调用会降低写效率。
④对象流:
对象流也是一对高级流,提供的功能是读写 Java 中的任何对象。
-
对象输出流: java.io.ObjectOutputStream
-
它可以将给定的 java 对象转换为一组字节,然后通过其他链接的流将这些字节写出。
- 当一个类的实例希望可以被对象流进行读写,那么该类必须实现 java.io.Serializable 接口。
import java.io.Serializable;
import java.io.Serial;
public class Test implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
……
}
当一个类实现了 Serializable 接口之后,要求应当定义一个常量 serialVersionUID ,即:序列化版本号。
-
实际举例: import java.io.*;
public class TestForIO {
public static void main(String[] args) throws IOException {
Test test = new Test("Tom",18);
FileOutputStream fileOutputStream = new FileOutputStream("test.txt");
ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
objectOutputStream.writeObject(test);
System.out.println("game over!");
objectOutputStream.close();
}
}
import java.io.Serializable;
import java.util.Objects;
public class Test implements Serializable {
private String name;
private int age;
Test(String name,int age){
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Test{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Test test = (Test) o;
return age == test.age && Objects.equals(name, test.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
}
通过对象流写出对象的这个方法经历了两个步骤:
-
对象输入流:
可以进行对象的反序列化操作;使用对象流读取的字节必须是通过对象输出流序列化的一组字节。
java.io.ObjectInputStream
import java.io.*;
public class TestForIO {
public static void main(String[] args) throws IOException, ClassNotFoundException {
FileInputStream fileInputStream = new FileInputStream("test.txt");
ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
Test t = (Test) objectInputStream.readObject();
System.out.println(t);
objectInputStream.close();
}
}
⑤序列化相关关键字介绍:
transient
transient关键字修饰的属性在对象序列化时会被忽略;忽略不必要的属性可以达到对象“瘦身”的作用
public class Test implements Serializable {
private transient String name;
private int age;
@Serial
private static final long serialVersionUID = 1L;
……
}
- 当前类中所有引用类型的属性,他们对应的类也必须实现 Serializable 接口。(譬如上面的 String 类)
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence,
Constable, ConstantDesc {
……
}
⑥字符流Reader和Writer:
- Java 将流按照读写单位又进行了一种划分方式——字节流与字符流。字节流的读写单位是字节,而字符流的读写单位是字符。所以字符流只适合读写文本数据。
java.io.Reader;
java.io.Writer;
这两个类也是抽象类,是所有字符输入流与字符输出流的父类,规定了所有读写字符的相关方法。

十二、异常处理机制:
①基本介绍:
当程序中抛出一个异常后,程序从程序中导致异常的代码处跳出,java 虚拟机检测寻找和 try 关键字匹配的处理该异常的 catch 块,如果找到,将控制权交到 catch 块中的代码,然后继续往下执行程序,try块中发生异常的代码不会重新被执行。如果没有找到处理该异常的 catch 块,在所有的 finally 块代码被执行和当前线程所属的 ThreadGroup 的 uncaughtException 方法被调用后,遇到异常的当前线程被中止。
②try_catch:
-
基本介绍:
- try{…}语句制指定了一段代码,该段代码就是一次捕获并处理例外的范围。
- 在执行过程中,该段代码可能会产生并抛出一种或几种类型的异常对象,它后面的 catch 语句分别对这些异常做相应的处理。
- 如果没有异常产生,所有的 catch 代码段都被略过不执行。
- 在 catch 语句块中是对异常进行处理的代码。
- 在 catch 中声明的异常对象(catch(SomeException e))封装了异常事件发生的信息,在 catch 语句块中可以使用这个对象的一些方法获取这些信息。
-
代码举例(未使用try-catch):
- Java 异常处理机制中的 try-catch;try 语句块用来包含可能出错的代码片段, catch 用来捕获这些错误并针对该错误进行处理。
public class TestForTryCatch {
public static void main(String[] args) {
System.out.println("Start!");
String string = null;
System.out.println(string.length());
System.out.println("End!");
}
}

-
代码举例(使用try-catch): public class TestForTryCatch {
public static void main(String[] args) {
System.out.println("Start!");
try{
String string = "";
System.out.println(string.length());
System.out.println(string.charAt(0));
System.out.println("!!!!!!");
}catch (NullPointerException e){
System.out.println("出现了空指针!");
}catch (StringIndexOutOfBoundsException e){
System.out.println("字符串下标越界了!");
}catch (Exception e){
System.out.println("出现了未知的错误!");
}
System.out.println("End!");
}
}
注意事项:
- try 语句块中出错代码以下内容都不执行(譬如上面代码中的**System.out.println("!!!");**语句)。
- try 语句块中代码要是没有出错时,catch 语句块都不会执行。
- 为了捕获多种类型的异常,catch 语句块是可以书写多个的。
- 应应当在当在最后一个 catch 处捕获 Exception ;尽量避免一个未捕获异常导致程序中断。
③finally:
finally 块是异常处理机制的最后一块,可以直接跟在 try 之后或者最后一个 catch 之后。finally 可以确保只要程序运行到 try 语句块中,那么无论是否抛出异常,finally 中的代码必定执行。
-
代码举例: public class TestForTryCatch {
public static void main(String[] args) {
System.out.println("Start!");
try{
String string = "hhhh";
System.out.println(string.length());
}catch (NullPointerException e){
System.out.println("出现了空指针!");
}finally {
System.out.println("finally!");
}
System.out.println("End!");
}
}
- 上面的代码中,将 finally 直接去除也能达到相同的效果;但是出现下面的情形就非 finally 不可了。
public class TestForTryCatch {
public static void main(String[] args) {
System.out.println("Start!");
try{
String string = "hhhh";
System.out.println(string.length());
return;
}catch (NullPointerException e){
System.out.println("出现了空指针!");
}finally {
System.out.println("finally!");
}
System.out.println("End!");
}
}
-
实际应用: import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class TestForTryCatch {
public static void main(String[] args) {
FileOutputStream fileOutputStream = null;
try {
fileOutputStream = new FileOutputStream("test.txt");
fileOutputStream.write('A');
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally{
try {
if (fileOutputStream!=null)
fileOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
-
FileOutputStream 对象的定义要放在 try 语句块的外面,在 try 语句块中定义的话,那么 finally 语句块中将因为变量作用权限的问题,无法识别该对象。 FileOutputStream fileOutputStream = null;
-
通过观察 FileNotFoundException 的源代码,我们可以发现 该类是继承于 IOException 的,所以对于异常的捕获没有更加精细的要求的话,对于 FileNotFoundException 异常的捕获是可以省去的。 public class FileNotFoundException extends IOException{
……
}
-
但是如果为了更加准确的捕获异常的信息,则派生类(子类)要在超类(父类)之前进行捕获判断。 catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
个人理解:就像下网捕鱼一样,一张大、小鱼都能捕获的网在前面,后面只能捕获小鱼的网怎么能抓到鱼。所以捕获的“层级”要逐步提升。
-
因为当 try 语句块中某一句程序报错被捕获之后,其后面的程序都不会被运行,所以为了保证文件流的关闭,需要借助 finally 语句块。 finally{
try {
if (fileOutputStream!=null)
fileOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
之所以在 close 方法周围又来一个异常处理程序,是 java 为了 close 没有被正确执行所做的预防性策略。
-
针对上面代码的优化:
-
在 JDK1.7 之后推出了一个新的特性——autoclose;允许编译器在编译过程中自动处理诸如流的关闭工作。因此上面的代码可以改写为: import java.io.FileOutputStream;
import java.io.IOException;
public class TestForTryCatch {
public static void main(String[] args) {
try(FileOutputStream fileOutputStream = new FileOutputStream("test.txt");
){
fileOutputStream.write('B');
}catch (IOException e){
e.printStackTrace();
}
}
}
-
需要关闭的对象,皆定义在“()”中,编译器会帮我们自动处理关闭工作,最终会将代码改变,在 finally 中将其关闭。那么什么是需要关闭的对象呢? -
凡是实现了 AutoCloseable接口的类的对象才是需要关闭的对象。(譬如上面的 FileOutputStream ) public class FileOutputStream extends OutputStream{
……
}
public abstract class OutputStream implements Closeable, Flushable {
……
}
public interface Closeable extends AutoCloseable {
……
}
-
常见的实现了 AutoCloseable接口的类有——JavaIO中的所有流、RandomAccessFile …… -
final、finally、finalize之前的区别:
- final 修饰在类上不能被继承,修饰在方法上不能被重写,修饰在变量上不能被更改。
- finally 块是异常处理机制的最后一块,可以直接跟在 try 之后或者最后一个 catch 之后。finally 可以确保只要程序运行到 try 语句块中,那么无论是否抛出异常,finally 中的代码必定执行。
- 每个类中都有 finalize 方法,因为这个方法是在 object 中定义的方法;当一个对象被 GC 回收之前调用的最后一个方法。
-
一个笔试题: public class TestForTryCatch {
public static void main(String[] args) {
System.out.println(test("0")+","+test(null)+","+test(""));
}
public static int test(String str){
try{
return str.charAt(0)-'0';
}catch (NullPointerException e){
return 1;
}catch (Exception e){
return 2;
}finally {
return 3;
}
}
}
- 因为要确保 finally 语句块中语句一定会执行,所以 try、catch 中的语句会执行,但是他们的 return 值会被 finally 语句块中的 return “洗掉”;所以最终 return 的是3。所以 finally 语句块中不推荐使用 return 语句,使用了会导致 try-catch 语句块中的 return 语句全部“没用”。
④异常抛出:
-
throw关键字:
-
当程序发生错误无法处理时,会抛出对应的异常对象,除此之外,在某些时刻,可能会想要自行抛出异常。例如在异常处理结束之后,再将异常抛出,让下一层异常处理块来捕捉,若想要自行抛出异常,可以使用 throw 关键字,并生成指定的异常对象后抛出。 public class Person {
private int age;
public int getAge() {
return age;
}
public void setAge(int age) {
if (age<0||age>100){
throw new RuntimeException("年龄数据不合理!");
}
this.age = age;
}
}
public class TestForTryCatch {
public static void main(String[] args) {
Person p = new Person();
p.setAge(10086);
System.out.println(p.getAge());
}
}
- 但是这里有一个弊端,虽然 setAge 方法当数据取值不合理时会抛出异常,但是我调用该方法的程序并不知道这个会抛出异常,也就不会设置相关的 try-catch 程序。所以为了解决这个弊端,我们需要使用到下面介绍的 throws 关键字。
-
throws关键字:
-
程序中会声明许多方法,这些方法中可能会因某些错误而引发异常,但不希望直接在这个方法中处理这些异常,而希望调用这个它的方法来统一处理,这个时候可以使用 throws 关键字来声明这个方法将会抛出异常。(换句话说就是通过 throws 来通知调用该方法的方法做好处理异常的准备,设置好 try-catch 程序。) public class Person {
private int age;
public int getAge() {
return age;
}
public void setAge(int age) throws Exception {
if (age<0||age>100){
throw new Exception("年龄数据不合理!");
}
this.age = age;
}
}
public class TestForTryCatch {
public static void main(String[] args) {
Person p = new Person();
try {
p.setAge(10086);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(p.getAge());
}
}
- 通常一个方法中使用 throw 抛出一个异常时,就要在方法声明时使用 throws 声明该异常的抛出以通知调用者解决该异常。
- 只有抛出 RuntimeException (运行时异常)及其子类型异常时可以不要求这样做。(后面会解释)
-
throws 小结:
- 当调用一个含有 throws 声明异常抛出的方法时,要求必须处理该异常;而处理方式有两种:
- 使用 try-catch 捕获并解决异常。
- 在当前方法上继续使用 throws 声明该异常的抛出。
-
重写超类含有 throws 声明异常抛出的方法时对于 throws 的重写规则: import java.awt.*;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.sql.SQLClientInfoException;
public class Person {
public void doSomething() throws IOException, AWTException{
}
}
class Son extends Person{
}
-
RuntimeException(运行时异常):
异常 | 说明 |
---|
IllegalArgumentException | 抛出的异常表明向方法传递了一个不合法或不正确的参数。 | NullPointerException | 当应用程序试图在需要对象的地方使用 null 时,抛出该异常。 | ArrayIndexOutOfBoundsException | 当使用的数组下标超过数组允许范围时,抛出该异常。 | ClassCastException | 当试图将对象强制转换为不是实例的子类时,抛出该异常。 | NumberFormatException | 当应用程序试图将字符串转换成一种数值类型,但该字符串不能转换为适当格式时,抛出该异常。 |
⑤Exception常用API:
-
printStackTrace 方法:输出错误信息。 public class TestForTryCatch {
public static void main(String[] args) {
try{
String string = "A";
System.out.println(Integer.parseInt(string));
}catch (Exception e){
e.printStackTrace();
}
}
}
-
getMessage 方法:获取错误消息。 public class TestForTryCatch {
public static void main(String[] args) {
try{
String string = "A";
System.out.println(Integer.parseInt(string));
}catch (Exception e){
System.out.println(e.getMessage());
}
}
}
⑥自定义异常:
通常是用来说明当前项目的某个业务逻辑错误。
-
首先定义自定义异常时,需要将其继承现有的一个异常类。 public class IllegalAgeException extends Exception{
……
}
-
然后自定义异常类中类体的声明可以借助编译器快捷实现。  -
最后使用时是和其他java给定的异常类一样使用的。
public class IllegalAgeException extends Exception{
public IllegalAgeException() {
}
public IllegalAgeException(String message) {
super(message);
}
public IllegalAgeException(String message, Throwable cause) {
super(message, cause);
}
public IllegalAgeException(Throwable cause) {
super(cause);
}
public IllegalAgeException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
public class Person {
private int age;
public int getAge() {
return age;
}
public void setAge(int age) throws IllegalAgeException {
if (age<0||age>100){
throw new IllegalAgeException("年龄不符合!");
}
this.age = age;
}
}
public class TestForTryCatch {
public static void main(String[] args) {
Person p = new Person();
try {
p.setAge(10086);
} catch (IllegalAgeException e) {
e.printStackTrace();
}
System.out.println(p.getAge());
}
}
未完待续……
|