异常机制
基本概念
java.lang.Throwable类(超类) :
- 错误(Error) : Java虚拟机无法解决的严重错误,通常无法编码解决
- 异常(Exception) : 描述因编程错误或偶然外在因素导致的轻微错误,通常可以编码解决
- java.lang.Exception类(超类)
- RuntimeException - 运行时异常,也叫作非检测性异常
- ArithmeticException类 - 算术异常
- ArrayIndexOutOfBoundsException类 - 数组下标越界异常
- NullPointerException - 空指针异常
- ClassCastException - 类型转换异常
- NumberFormatException - 数字格式异常
- IOException和其它异常 - 其它异常,也叫作检测性异常,所谓检测性异常就是指在编译阶段都能被编译器检测出来的异常。
注意:
当程序执行过程中发生异常但又没有手动处理时,则由Java虚拟机采用默认方式处理异常,而默认处理方式就是:打印异常的名称、异常发生的原因、异常发生的位置以及终止程序。
// 非检测性异常-运行时异常
System.out.println(5/0); //java.lang.ArithmeticException: / by zero
int[] arr = new int[5];
System.out.println(arr[5]); //java.lang.ArrayIndexOutOfBoundsException: Index 5 out of bounds for length 5
String str = null;
System.out.println(str.length()); // java.lang.NullPointerException
Exception ex = new Exception();
IOException ie = (IOException)ex; //java.lang.ClassCastException
System.out.println(Integer.parseInt("123a")); //java.lang.NumberFormatException
捕获
FileInputStream fis = null;
try {
fis = new FileInputStream("./a.txt");
} catch (FileNotFoundException e) {
e.printStackTrace(); //java.io.FileNotFoundException: ./a.txt (No such file or directory)
}
try {
fis.close();
} catch (IOException e) {
e.printStackTrace(); // 没有处理 java.lang.NullPointerException 程序终止
} catch (NullPointerException e) {
e.printStackTrace(); // 处理 java.lang.NullPointerException 程序可以正常向下执行
} catch (Exception e) {
e.printStackTrace(); // 放到最后
} finally {
//编写无论是否发生异常都要执行的代码;
}
注意事项
-
当需要编写多个catch分支时,切记小类型应该放在大类型的前面;
-
懒人的写法:catch(Exception e) {}
-
finally通常用于进行善后处理,如:关闭已经打开的文件等
*/ // 执行流程 try { a; b; - 可能发生异常的语句 c; }catch(Exception ex) { d; }finally { e; } 当没有发生异常时的执行流程:a b c e; 当发生异常时的执行流程:a b d e;
finally return
// 1. 返回 -2
private static int withFinally() {
int len = 0;
try {
String s = "abc";
s.length();
return len;
} catch (NullPointerException e) {
len = 1;
return len;
} finally {
System.out.println("执行finally语句");
len = -2;
return len;
}
}
// 2 返回 0
private static int withFinally() {
int len = 0;
try {
String s = "abc";
s.length();
return len;
} catch (NullPointerException e) {
len = 1;
return len;
} finally {
System.out.println("执行finally语句");
// finally里给return用的变量值赋值没用
len = -2;
//return len;
}
}
// 3 返回 1
private static int withFinally() {
int len = 0;
try {
String s = null;
s.length();
return len;
} catch (NullPointerException e) {
len = 1;
return len;
} finally {
System.out.println("执行finally语句");
// finally里给return用的变量值赋值没用
len = -2;
//return len;
}
}
// 4 返回 -2
private static int withFinally() {
int len = 0;
try {
String s = null;
s.length();
return len;
} catch (NullPointerException e) {
len = 1;
return len;
} finally {
System.out.println("执行finally语句");
len = -2;
return len;
}
}
抛出
在某些特殊情况下有些异常不能处理或者不便于处理时,就可以将该异常转移给该方法的调用者,这种方法就叫异常的抛出。当方法执行时出现异常,则底层生成一个异常类对象抛出,此时异常代码后续的代码就不再执行。
public static void show() throws IOException {}
-
方法重写的原则
-
要求方法名相同、参数列表相同以及返回值类型相同,从jdk1.5开始支持返回子类类型;
-
要求方法的访问权限不能变小,可以相同或者变大;
-
要求方法不能抛出更大的异常;
- 子类重写的方法不能抛出更大的异常、不能抛出平级不一样的异常,但可以抛出一样的异常、更小的异常以及不抛出异常。
经验
-
若父类中被重写的方法没有抛出异常时,则子类中重写的方法只能进行异常的捕获处理。
-
若一个方法内部又以递进方式分别调用了好几个其它方法,则建议这些方法内可以使用抛出的方法处理到最后一层进行捕获方式处理。
-
自定义异常
当需要在程序中表达年龄不合理的情况时,而Java官方又没有提供这种针对性的异常,此时就需要程序员自定义异常加以描述。
// 定义
public class AgeException extends Exception {
static final long serialVersionUID = 7818375828146090155L; // 序列化的版本号 与序列化操作有关系
public AgeException() {
}
public AgeException(String message) {
super(message);
}
}
// 使用
public void setAge(int age) throws AgeException {
if (age > 0 && age < 150) {
this.age = age;
} else {
throw new AgeException("年龄不合理哦!!!");
}
}
public void setAge(int age) {
if (age > 0 && age < 150) {
this.age = age;
} else {
try {
throw new AgeException("年龄不合理哦!!!");
} catch (AgeException e) {
e.printStackTrace();
}
}
}
//Java采用的异常处理机制是将异常处理的程序代码集中在一起,与正常的程序代码分开,使得程序简洁、优雅,并易于维护。
File类
java.io.File类主要用于描述文件或目录路径的抽象表示信息,可以获取文件或目录的特征信息,如:大小等。
- 常用方法
**方法声明 ** | 功能概述 |
---|---|
File(String pathname) | 根据参数指定的路径名来构造对象 |
File(String parent, String child) | 根据参数指定的父路径和子路径信息构造对象 |
File(File parent, String child) | 根据参数指定的父抽象路径和子路径信息构造对象 |
boolean exists() | 测试此抽象路径名表示的文件或目录是否存在 |
String getName() | 用于获取文件的名称 |
long length() | 返回由此抽象路径名表示的文件的长度 |
long lastModifified() | 用于获取文件的最后一次修改时间 |
String getAbsolutePath() | 用于获取绝对路径信息 |
boolean delete() | 用于删除文件,当删除目录时要求是空目录 |
// 文件类
public static void main(String[] args) throws IOException {
// 1. File(String pathname) 根据参数指定的路径名来构造对象
File f1 = new File("./a.txt");
// boolean exists() 文件是否存在
if (f1.exists()) {
// String getName() 用于获取文件的名称
System.out.println("名称:"+f1.getName());
// long length() 返回由此抽象路径名表示的文件的长度
System.out.println("大小:"+f1.length());
// long lastModified() 用于获取文件的最后一次修改时间
Date d1 = new Date(f1.lastModified());
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println("最后一次修改:"+sdf.format(d1));
// String getAbsolutePath() 用于获取绝对路径信息
System.out.println("绝对路径:"+f1.getAbsolutePath());
// boolean delete() 用于删除文件,当删除目录时要求是空目录
System.out.println("删除:"+f1.delete());
}else {
// boolean createNewFile() 用于创建新的空文件
System.out.println(f1.createNewFile()?"文件创建成功":"文件创建失败");
}
}
// 目录类
// 1
File f = new File("./test");
if (f.exists()) {
System.out.println("目录:"+f.getName());
System.out.println("删除:"+f.delete());
}else {
// boolean mkdir() 用于创建目录 单个目录
System.out.println(f.mkdir()?"目录创建成功":"目录创建失败");
}
// 2
File f = new File("./test/test1/test2/test3");
if (f.exists()) {
System.out.println("目录:"+f.getName());
// 多级目录只删除最内层
System.out.println("删除:"+f.delete());
}else {
// boolean mkdirs() 用于创建多级目录
System.out.println(f.mkdirs()?"目录创建成功":"目录创建失败");
}
// 遍历
File f = new File("./test");
// File[] listFiles() 获取该目录下的所有内容
File[] fileArray = f.listFiles();
for (File tf : fileArray) {
String name = tf.getName();
// boolean isFile() 判断是否为文件
if (tf.isFile()) {
System.out.println(name);
}
if (tf.isDirectory()) {
System.out.println("目录"+name);
}
}
// 过滤器
FileFilter fileFilter = (File pathname) -> {return pathname.getName().endsWith("c.txt");};
// File[] listFiles(FileFilter filter) 获取目录下满足筛选器的所有内容
File[] files = f.listFiles(fileFilter);
for (File tf : files ) {
System.out.println(tf);
}
IO流
IO就是Input和Output的简写,也就是输入和输出的含义。
IO流就是指读写数据时像流水一样从一端流到另外一端,因此得名为“流"。
分类
- 按照读写数据的基本单位不同,分为 字节流 和 字符流。
- 其中字节流主要指以字节为单位进行数据读写的流,可以读写任意类型的文件。
- 其中字符流主要指以字符(2个字节)为单位进行数据读写的流,只能读写文本文件。
- 按照读写数据的方向不同,分为 输入流 和 输出流(站在程序的角度)
- 其中输入流主要指从文件中读取数据内容输入到程序中,也就是读文件。
- 其中输出流主要指将程序中的数据内容输出到文件中,也就是写文件。
- 按照流的角色不同分为节点流和处理流。
- 其中节点流主要指直接和输入输出源对接的流。
- 其中处理流主要指需要建立在节点流的基础之上的流。
体系结构
重点
FileWriter
java.io.FileWriter类主要用于将文本内容写入到文本文件。
方法声明 | 功能介绍 |
---|---|
FileWriter(String fifileName) | 根据参数指定的文件名构造对象 |
FileWriter(String fifileName, boolean append) | 以追加的方式根据参数指定的文件名来构造对象 |
void write(int c) | 写入单个字符 |
void write(char[] cbuf, int offff, int len) | 将指定字符数组中从偏移量offff开始的len个字符写入此文件输出流 |
void write(char[] cbuf) | 将cbuf.length个字符从指定字符数组写入此文件输出流中 |
void flflush() | 刷新流 |
void close() | 关闭流对象并释放有关的资源 |
FileWriter fw = null;
try {
/**
* FileWriter(String fileName) 根据参数指定的文件名构造对象
* 不存在-创建 存在-清空原有文件
*/
fw = new FileWriter("./a.txt");
/**
* FileWriter(String fileName, boolean append) 以追加的方式根据参数指定的文件名来构造对象
* 不存在-创建 存在-保留原有内容
*/
fw = new FileWriter("./a.txt",true);
/**
* void write(int c) 写入单个字符
*/
fw.write("a");
char[] cArr = new char[]{'a','b','c','d'};
/**
* void write(char[] cbuf, int off, int len) 将指定字符数组中从偏移量off开始的len个字符写入此
* 文件输出流
*/
fw.write(cArr ,1,2);
/**
* void write(char[] cbuf) 将cbuf.length个字符从指定字符数组写入此文件输出流中
*/
fw.write(cArr);
/**
* void flush() 刷新流
*/
fw.flush();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
/**
* void close() 关闭流对象并释放有关的资源
*/
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
FileReader
java.io.FileReader类主要用于从文本文件读取文本数据内容。
方法声明 | 功能介绍 |
---|---|
FileReader(String fileName) | 根据参数指定的文件名构造对象 |
int read() | 读取单个字符的数据并返回,返回-1表示读取到末尾 |
int read(char[] cbuf, int offffset, int length) | 从输入流中将最多len个字符的数据读入一个字符数组中,返回读取到的字符个数,返回-1表示读取到末尾 |
int read(char[] cbuf) | 从此输入流中将最多 cbuf.length 个字符的数据读入字符数组中,返回读取到的字符个数,返回-1表示读取到末尾 |
void close() | 关闭流对象并释放有关的资源 |
FileReader fr = null;
try {
/**
* FileReader(String fileName) 根据参数指定的文件名构造对象
*/
fr = new FileReader("./a.txt");
/**
* int read() 读取单个字符的数据并返回,返回-1表示读取到末尾
*/
int res;
//while ((res = fr.read()) != -1) {
// System.out.println((char)res);
//}
/**
*int read(char[] cbuf, int offset, int length)
*从输入流中将最多len个字符的数据读入一个字符数组中,返回读取到的字符个数,返回-1表示读取到末尾
*/
char[] cArr = new char[5];
//res = fr.read(cArr,1,3);
//System.out.println(res+ Arrays.toString(cArr));
/**
* int read(char[] cbuf)
* 从此输入流中将最多 cbuf.length 个字符的数据读入字符数组中,返回读取到的字符个数,返回-1表示读取到末尾
*/
res = fr.read(cArr);
System.out.println(res+ Arrays.toString(cArr));
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
/**
* void close() 关闭流对象并释放有关的资源
*/
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
BufferedWriter BufferedReader
java.io.BufferedWriter类主要用于写入单个字符、字符数组以及字符串到输出流中。
java.io.BffferedReader类用于从输入流中读取单个字符、字符数组以及字符串。
方法声明 | 功能介绍 |
---|---|
BufferedWriter(Writer out) | 根据参数指定的引用来构造对象 |
BufferedWriter(Writer out, int sz) | 根据参数指定的引用和缓冲区大小来构造对象 |
void write(int c) | 写入单个字符到输出流中 |
void write(char[] cbuf, int off, int len) | 将字符数组cbuf中从下标offff开始的len个字符写入输出流 |
void write(char[] cbuf) | 将字符串数组cbuf中所有内容写入输出流中 |
void write(String s, int off, int len) | 将参数s中下标从offff开始的len个字符写入输出流中 |
void write(String str) | 将参数指定的字符串内容写入输出流中 |
void newLine() | 用于写入行分隔符到输出流中 |
void flush() | 刷新流 |
void close() | 关闭流对象并释放有关的资源 |
BufferedReader(Reader in) | 根据参数指定的引用来构造对象 |
BufferedReader(Reader in, int sz) | 根据参数指定的引用和缓冲区大小来构造对象 |
int read() | 从输入流读取单个字符,读取到末尾则返回-1,否则返回实际读取到的字符内容 |
int read(char[] cbuf, int off, int len) | 从输入流中读取len个字符放入数组cbuf中下标从offff开始的位置上,若读取到末尾则返回-1,否则返回实际读取到的字符个数 |
int read(char[] cbuf) | 从输入流中读满整个数组cbuf |
String readLine() | 读取一行字符串并返回,返回null表示读取到末尾 |
void close() | 关闭流对象并释放有关的资源 |
public static void fileCopyByBufferedChar(String fromPath ,String toPath) throws IOException {
BufferedReader br = new BufferedReader(new FileReader(fromPath));
BufferedWriter bw = new BufferedWriter(new FileWriter(toPath));
String str = null;
// 读取一行字符串并返回,返回null表示读取到末尾
while ((str = br.readLine()) != null) {
bw.write(str);
// 用于写入行分隔符到输出流中
bw.newLine();
}
bw.close();
br.close();
}
FileOutputStream FileInputStream
java.io.FileOutputStream类主要用于将图像数据之类的原始字节流写入到输出流中。
java.io.FileInputStream类主要用于从输入流中以字节流的方式读取图像数据等。
方法声明 | 功能介绍 |
---|---|
FileInputStream | |
FileInputStream(String name) | 根据参数指定的文件路径名来构造对象 |
int read() | 从输入流中读取单个字节的数据并返回,返回-1表示读取到末尾 |
int read(byte[] b, int off, int len) | 从此输入流中将最多len个字节的数据读入字节数组中,返回读取到的字节个数,返回-1表示读取到末尾 |
nt read(byte[] b) | 从此输入流中将最多 b.length 个字节的数据读入字节数组中,返回读取到的字节个数,返回-1表示读取到末尾 |
void close() | 关闭流对象并释放有关的资源 |
int available() | 获取输入流所关联文件的大小 |
FileOutputStream | |
FileOutputStream(String name) | 根据参数指定的文件名来构造对象 |
FileOutputStream(String name,boolean append) | 以追加的方式根据参数指定的文件名来构造对象 |
void write(int b) | 将指定字节写入此文件输出流 |
void write(byte[] b, int off, int len) | 将指定字节数组中从偏移量offff开始的len个字节写入此文件输出流 |
void write(byte[] b) | 将 b.length 个字节从指定字节数组写入此文件输出流中 |
void flush() | 刷新此输出流并强制写出任何缓冲的输出字节 |
void close() | 关闭流对象并释放有关的资源 |
public static void fileCopyByByte(String fromPath ,String toPath) throws IOException {
FileInputStream fin = new FileInputStream(fromPath);
FileOutputStream fout = new FileOutputStream(toPath);
/**
// 每次一个字节读取 文件大时 效率低
int res = 0;
while ((res = fin.read()) != -1) {
fout.write(res);
}
*/
/**
// 文件过大时无法申请和文件大小一样的缓冲区 真实物理空间不足
int len = fin.available();//获取输入流所关联文件的大小
byte[] brr = new byte[len];
int res = fin.read(brr);
fout.write(brr);
*/
// 适当的缓冲区
byte[] bArr = new byte[1024];
int res =0 ;
while ((res = fin.read(bArr))!=-1) {
fout.write(bArr,0,res);
}
if (null != fout) {
fout.close();
}
if (null != fin) {
fin.close();
}
}
BufferedOutputStream BufferedInputStream
java.io.BufferedOutputStream类主要用于描述缓冲输出流,此时不用为写入的每个字节调用底层系统。
java.io.BufferedInputStream类主要用于描述缓冲输入流。
方法声明 | 功能介绍 |
---|---|
BufferedInputStream | |
BufffferedInputStream(InputStream in) | 根据参数指定的引用构造对象 |
BufffferedInputStream(InputStream in, int size) | 根据参数指定的引用和缓冲区大小构造对象 |
int read() | 读取单个字节 |
int read(byte[] b, int off, int len) | 读取len个字节 |
int read(byte[] b) | 读取b.length个字节 |
void close() | 关闭流对象并释放有关的资源 |
BufferedOutputStream | |
BufffferedOutputStream(OutputStream out) | 根据参数指定的引用来构造对象 |
BufffferedOutputStream(OutputStream out, int size) | 根据参数指定的引用和缓冲区大小来构造对象 |
void write(int b) | 写入单个字节 |
void write(byte[] b, int offff, int len) | 写入字节数组中的一部分数据 |
void write(byte[] b) | 写入参数指定的整个字节数组 |
void flush() | 刷新流 |
void close() | 关闭流对象并释放有关的资源 |
public static void fileCopyByBufferedByet(String fromPath ,String toPath) throws IOException {
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(fromPath));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(toPath));
byte[] bArr = new byte[1024];
int res =0 ;
while ((res = bis.read(bArr))!=-1) {
bos.write(bArr,0,res);
}
bos.close();
bis.close();
}
PrintStream
java.io.PrintStream类主要用于更加方便地打印各种数据内容。
java.io.PrintStream | |
---|---|
PrintStream(OutputStream out) | 根据参数指定的引用来构造对象 |
void print(String s) | 用于将参数指定的字符串内容打印出来 |
void println(String x) | 用于打印字符串后并终止该行 |
void flush() | 刷新流 |
void close() | 用于关闭输出流并释放有关的资源 |
PrintStream ps = new PrintStream(new FileOutputStream("./a.txt", true));
PrintWriter
java.io.PrintWriter类主要用于将对象的格式化形式打印到文本输出流。
方法声明 | 功能介绍 |
---|---|
PrintWriter(Writer out) | 根据参数指定的引用来构造对象 |
void print(String s) | 将参数指定的字符串内容打印出来 |
void println(String x) | 打印字符串后并终止该行 |
void flush() | 刷新流 |
void close() | 关闭流对象并释放有关的资源 |
InputStreamReader OutputStreamWriter
java.io.OutputStreamWriter类主要用于实现从字符流到字节流的转换。
java.io.InputStreamReader类主要用于实现从字节流到字符流的转换。
java.io.OutputStreamWrite | |
---|---|
OutputStreamWriter(OutputStream out) | 根据参数指定的引用来构造对象 |
OutputStreamWriter(OutputStream out, String charsetName) | 根据参数指定的引用和编码构造对象 |
void write(String str) | 将参数指定的字符串写入 |
void flush() | 刷新流 |
void close() | 用于关闭输出流并释放有关的资源 |
InputStreamReader(InputStream in) | 根据参数指定的引用来构造对象 |
InputStreamReader(InputStream in, String charsetName) | 根据参数指定的引用和编码来构造对象 |
int read(char[] cbuf) | 读取字符数据到参数指定的数组 |
void close() | 用于关闭输出流并释放有关的资源 |
// 由手册可知: System.in代表键盘输入, 而且是InputStream类型的 字节流
InputStreamReader isr = new InputStreamReader(System.in);
// 由手册可知:构造方法需要的是Reader类型的引用,但Reader类是个抽象类,实参只能传递子类的对象 字符流
BufferedReader br = new BufferedReader(isr);
字符编码
- ASCII:美国标准信息交换码, 使用一个字节的低7位二位进制进行表示。
- ISO8859-1:拉丁码表,欧洲码表,使用一个字节的8位二进制进行表示。
- GB2312:中国的中文编码表,最多使用两个字节16位二进制为进行表示。
- GBK:中国的中文编码表升级,融合了更多的中文文字符号,最多使用两个字节16位二进制位表示。
- Unicode:国际标准码,融合了目前人类使用的所有字符,为每个字符分配唯一的字符码。所有的文字都用两个字节16位二进制位来表示。
DataOutputStream DataInputStream
java.io.DataInputStream类主要用于从输入流中读取基本数据类型的数据。
java.io.DataOutputStream类主要用于以适当的方式将基本数据类型写入输出流中。
方法声明 | 功能介绍 |
---|---|
DataInputStream(InputStream in) | 根据参数指定的引用来构造对象 InputStream类是抽象类,实参需要传递子类对象 |
int readInt() | 用于从输入流中一次性读取一个整数数据并返回 |
void close() | 用于关闭文件输出流并释放有关的资源 |
DataOutputStream(OutputStream out) | 根据参数指定的引用构造对象 OutputStream类是个抽象类,实参需要传递子类对象 |
void writeInt(int v) | 用于将参数指定的整数一次性写入输出流,优先写入高字节 |
void close() | 用于关闭文件输出流并释放有关的资源 |
DataOutputStream dos = new DataOutputStream(new FileOutputStream("./a.txt"));
// 66: 0000 0000 ... 0100 0010 => B
int num = 66;
dos.writeInt(num); // 写入4个字节
dos.write(num); // 写入1个字节
DataInputStream dis = new DataInputStream(new FileInputStream("d:/a.txt"));
int res = dis.readInt(); // 读取4个字节
int res = dis.read(); // 读取1个字节
ObjectOutputStream
- java.io.ObjectOutputStream类主要用于将一个对象的所有内容整体写入到输出流中。
- 只能将支持 java.io.Serializable 接口的对象写入流中。
- 类通过实现 java.io.Serializable 接口以启用其序列化功能。
- 所谓序列化主要指将一个对象需要存储的相关信息有效组织成字节序列的转化过程
方法声明 | 功能介绍 |
---|---|
ObjectOutputStream(OutputStream out) | 根据参数指定的引用来构造对象 |
void writeObject(Object obj) | 用于将参数指定的对象整体写入到输出流中 |
void close() | 用于关闭输出流并释放有关的资源 |
ObjectInputStream
- java.io.ObjectInputStream类主要用于从输入流中一次性将对象整体读取出来。
- 所谓反序列化主要指将有效组织的字节序列恢复为一个对象及相关信息的转化过程。
方法声明 | 功能介绍 |
---|---|
ObjectInputStream(InputStream in) | 根据参数指定的引用来构造对象 |
Object readObject() | 主要用于从输入流中读取一个对象并返回 无法通过返回值来判断是否读取到文件的末尾 |
void close() | 用于关闭输入流并释放有关的资源 |
序列化
序列化机制是通过在运行时判断类的serialVersionUID来验证版本一致性的。在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地相应实体类的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常(InvalidCastException)。
transient关键字
transient是Java语言的关键字,用来表示一个域不是该对象串行化的一部分。当一个对象被串行化的时候,transient型变量的值不包括在串行化的表示中,然而非transient型的变量是被包括进去的。
经验
当希望将多个对象写入文件时,通常建议将多个对象放入一个集合中,然后将集合这个整体看做一个对象写入输出流中,此时只需要调用一次readObject方法就可以将整个集合的数据读取出来,从而避免了通过返回值进行是否达到文件末尾的判断。
RandomAccessFile
java.io.RandomAccessFile类主要支持对随机访问文件的读写操作。
方法声明 | 功能介绍 |
---|---|
RandomAccessFile(String name, String mode) | 根据参数指定的名称和模式构造对象 r: 以只读方式打开. rw:打开以便读取和写入. rwd:打开以便读取和写入,同步文件内容的更新. rws:打开以便读取和写入,同步文件内容和元数据的更新 |
int read() | 读取单个字节的数据 |
void seek(long pos) | 用于设置从此文件的开头开始测量的文件指针偏移量 |
void write(int b) | 将参数指定的单个字节写入 |
void close() | 用于关闭流并释放有关的资源 |
// 1.创建RandomAccessFile类型的对象与d:/a.txt文件关联
RandomAccessFile raf = new RandomAccessFile("d:/a.txt", "rw");
// 2.对文件内容进行随机读写操作
// 设置距离文件开头位置的偏移量,从文件开头位置向后偏移3个字节 aellhello
raf.seek(3);
int res = raf.read();
System.out.println("读取到的单个字符是:" + (char)res); // a l
res = raf.read();
System.out.println("读取到的单个字符是:" + (char)res); // h 指向了e
raf.write('2'); // 执行该行代码后覆盖了字符'e'
System.out.println("写入数据成功!");
多线程
- 程序 = 数据结构+算法
- 进程 : 运行载内存中的可执行文件. 多进程-操作系统内部。 重量级,消息资源
- 线程: 进程内部的程序流。 多线程-进程内部 。 轻量级,共享进程资源
- 多线程: 时间片轮转法
创建
java.lang.Thread类
自定义类继承Thread类并重写run方法,然后创建该类的对象调用start方法。
自定义类实现Runnable接口并重写run方法,创建该类的对象作为实参来构造Thread类型的对象,然后使用Thread类型的对象调用start方法。
继承Thread类的方式代码简单,但是若该类继承Thread类后则无法继承其它类,而实现
Runnable接口的方式代码复杂,但不影响该类继承其它类以及实现其它接口,因此以后的开发中
推荐使用第二种方式。
/*
* Thread() 使用无参的方式构造对象
* 1.使用无参方式构造Thread类型的对象
* 由源码可知:Thread类中的成员变量target的数值为null。
public Thread() {
this(null, null, "Thread-" + nextThreadNum(), 0);
}
*/
Thread t1 = new Thread();
public class SubThread extends Thread {
@Override
public void run() {
//do;
}
}
Thread ts = new SubThread();
//Thread(String name) 根据参数指定的名称来构造对象
//Thread(Runnable target) 根据参数指定的引用来构造对象,其中Runnable是个接口类型
public class SubRunnableRun implements Runnable {
@Override
public void run() {
// do;
}
}
SubRunnableRun srr = new SubRunnableRun();
Thread t1 = new Thread(srr);// 由源码可知:经过构造方法的调用之后,Thread类中的成员变量target的数值为srr。
//Thread(Runnable target,String name) 根据参数指定引用和名称来构造对象
/*
void run()
1. 若使用Runnable引用构造了线程对象,调用该方法时最终调用接口中的版本
2. 若没有使用Runnable引用构造线程对象,调用该方法时则啥也不做
源码
public void run() {
if (target != null) {
target.run();
}
}
调用run方法测试,本质上就是相当于对普通成员方法的调用,因此执行流程就是run方法的代码执行完毕后才能继续向下执行
*/
t1.run(); // 啥也不做
ts.run(); // do
/*
void start() 用于启动线程,Java虚拟机会自动调用该线程的run方法
用于启动线程,Java虚拟机会自动调用该线程类中的run方法
相当于又启动了一个线程,加上执行main方法的线程是两个线程
*/
ts.start();
// 匿名内部类实现
new Thread(){
@Override
public void run() {};
}.start();
new Thread(new Runnable() {
@Override
public void run() { }
}).start();
new Thread(()->{do;}).start();
线程生命周期
常用方法
public static void main(String[] args) {
class thredTest extends Thread{
thredTest(String name) {
super(name);
}
@Override
public void run() {
// long getId() 获取调用对象所表示线程的编号
System.out.println(getId()); // 13
// String getName() 获取调用对象所表示线程的名称
System.out.println(getName()); // test1
// void setName(String name) 设置/修改线程的名称为参数指定的数值
setName("test2");
System.out.println(getName()); // test2
}
}
new thredTest("test1").start();
new Thread(()->{
// static Thread currentThread() 获取当前正在执行线程的引用
Thread t = Thread.currentThread();
System.out.println("RunnableIdName是:" + t.getId() + ", 名称是:" + t.getName());
},"RunnableIdName").start(); // RunnableIdName是:14, 名称是:RunnableIdName
// 获取当前正在执行线程的引用,当前正在执行的线程是主线程,也就是获取主线程的引用
Thread t1 = Thread.currentThread();
System.out.println("主线程的编号是:" + t1.getId() + ", 名称是:" + t1.getName()); //主线程的编号是:1, 名称是:main
}
static void yield() | 当前线程让出处理器(离开Running状态),使当前线程进入Runnable状态等待 |
---|---|
static void sleep(times) | 使当前线程从 Running 放弃处理器进入Block状态, 休眠times毫秒, 再返回到Runnable如果其他线程打断当前线程的Block(sleep), 就会发生InterruptedException。 |
int getPriority() | 获取线程的优先级 |
void setPriority(int newPriority) | 修改线程的优先级。优先级越高的线程不一定先执行,但该线程获取到时间片的机会会更多 一些 |
void join() | 等待该线程终止 |
void join(long millis) | 等待参数指定的毫秒数 |
boolean isDaemon() | 用于判断是否为守护线程 |
void setDaemon(boolean on) | 用于设置线程为守护线程 |
当子线程不是守护线程时,虽然主线程先结束了,但是子线程依然会继续执行,直到打印完毕所有数据为止*
当子线程是守护线程时,当主线程结束后,则子线程随之结束*
线程同步机制
在Java语言中使用synchronized关键字来实现同步/对象锁机制从而保证线程执行的原子性
- 使用同步代码块的方式实现部分代码的锁定
synchronized(类类型的引用) {
编写所有需要锁定的代码;
}
- 使用同步方法的方式实现所有代码的锁定。直接使用synchronized关键字来修饰整个方法即可
public synchronized void run() {}
// 等价于
public void run() { synchronized(this){整个方法体代码块} }
public synchronized static void test() {}
//等价于
public static void test() { synchronized (AccountThreadTest.class) {}} // 该类型对应的Class对象,由于类型是固定的,因此Class对象也是唯一的,因此可以实现同步
//静态方法与非静态方法同时使用了synchronized后它们之间是非互斥关系的。
//原因在于:静态方法锁的是类对象而非静态方法锁的是当前方法所属对象。
-
注意事项
- 多个需要同步的线程在访问同步块时,看到的应该是同一个锁对象引用。
- 在使用同步块时应当尽量减少同步范围以提高并发的执行效率。
线程安全类与线程不安全类
- StringBuffer类是线程安全的类,但StringBuilder类不是线程安全的类。
- Vector类和 Hashtable类是线程安全的类,但ArrayList类和HashMap类不是线程安全的类。
- Collections.synchronizedList() 和 Collections.synchronizedMap()等方法实现安全。
死锁
public void run(){
synchronized(a){ //持有对象锁a,等待对象锁b
synchronized(b){
//编写锁定的代码;
}
}
}
线程二执行的代码:
public void run(){
synchronized(b){ //持有对象锁b,等待对象锁a
synchronized(a){
//编写锁定的代码;
}
}
}
Lock
- 从Java5开始提供了更强大的线程同步机制—使用显式定义的同步锁对象来实现。
- java.util.concurrent.locks.Lock接口是控制多个线程对共享资源进行访问的工具。
- 该接口的主要实现类是ReentrantLock类,该类拥有与synchronized相同的并发性,在以后的线程安全控制中,经常使用ReentrantLock类显式加锁和释放锁
private ReentrantLock lock = new ReentrantLock(); // 获取锁
{
lock.lock(); // 开始加锁
// do;
lock.unlock(); // 实现解锁
}
-
比较
- Lock是显式锁,需要手动实现开启和关闭操作,而synchronized是隐式锁,执行锁定代码后自动释放。
- Lock只有同步代码块方式的锁,而synchronized有同步代码块方式和同步方法两种锁。
- 使用Lock锁方式时,Java虚拟机将花费较少的时间来调度线程,因此性能更好。
线程间同步
public void run() {
while (true) {
synchronized (this) {
// 每当有一个线程进来后先大喊一声,调用notify方法
notify();
// do;
try {
wait(); // 当前线程进入阻塞状态,自动释放对象锁,必须在锁定的代码中调用
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
生产者消费者
public synchronized void produceProduct() {
notify();
if (未满) {
// do;
} else {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public synchronized void consumerProduct() {
notify();
if (有货) {
// do;
} else {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
线程池
首先创建一些线程,它们的集合称为线程池,当服务器接受到一个客户请求后,就从线程池中取出一个空闲的线程为之服务,服务完后不关闭该线程,而是将该线程还回到线程池中。
// Callable接口 java.util.concurrent.Callable
// V call() 计算结果并返回
public class ThreadCallableTest implements Callable {
@Override
public Object call() throws Exception {
// do;
return xxx;
}
}
/**
java.util.concurrent.FutureTask类用于描述可取消的异步计算,该类提供了Future接口的基本实
现,包括启动和取消计算、查询计算是否完成以及检索计算结果的方法,也可以用于获取方法调用
后的返回结果
*/
ThreadCallableTest tct = new ThreadCallableTest();
// FutureTask(Callable callable) 根据参数指定的引用来创建一个未来任务
FutureTask ft = new FutureTask(tct);
Thread t1 = new Thread(ft);
t1.start();
Object obj = null;
try {
// V get() 获取call方法计算的结果
obj = ft.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
/**
- java.util.concurrent.Executors类
工具类和线程池的工厂类
1. static ExecutorService newCachedThreadPool() 创建一个可根据需要创建新线程的线程池
2. static ExecutorService newFixedThreadPool(int nThreads) 创建一个可重用固定线程数的线程池
3. static ExecutorService newSingleThreadExecutor() 创建一个只有一个线程的线程池
- java.util.concurrent.ExecutorService接口。 真正的线程池接口,主要实现类是ThreadPoolExecutor
1. void execute(Runnable command) 执行任务和命令,通常用于执行Runnable
2. Future submit(Callable task) 执行任务和命令,通常用于执行Callable
3. void shutdown() 启动有序关闭
*/
// 1.创建一个线程池
ExecutorService executorService = Executors.newFixedThreadPool(10);
// 2.向线程池中布置任务
executorService.submit(new ThreadCallableTest());
// 3.关闭线程池
executorService.shutdown();
网络编程
网络模型
协议
计算机在网络中实现通信就必须有一些约定或者规则,这种约定和规则就叫做通信协议,通信协议
可以对速率、传输代码、代码结构、传输控制步骤、出错控制等制定统一的标准
TCP
传输控制协议(Transmission Control Protocol),是一种面向连接的协议
- 建立连接 => 进行通信 => 断开连接
- 在传输前采用"三次握手"方式。
- 在通信的整个过程中全程保持连接,形成数据传输通道。
- 保证了数据传输的可靠性和有序性。
- 是一种全双工的字节流通信方式,可以进行大数据量的传输。
- 传输完毕后需要释放已建立的连接,发送数据的效率比较低。
UDP
用户数据报协议(User Datagram Protocol),是一种非面向连接的协议,类似于写信
1. 在通信的整个过程中不需要保持连接,其实是不需要建立连接。
- 不保证数据传输的可靠性和有序性。
- 是一种全双工的数据报通信方式,每个数据报的大小限制在64K内。
- 发送数据完毕后无需释放资源,开销小,发送数据的效率比较高,速度快。
IP地址
-
192.168.1.1 - 是绝大多数路由器的登录地址,主要配置用户名和密码以及Mac过滤。
-
IP地址是互联网中的唯一地址标识,本质上是由32位二进制组成的整数,叫做IPv4,当然也有128位二进制组成的整数,叫做IPv6,目前主流的还是IPv4。
-
日常生活中采用点分十进制表示法来进行IP地址的描述,将每个字节的二进制转化为一个十进制整数,不同的整数之间采用小数点隔开。
-
Windows系统:在dos窗口中使用ipconfifig或ipconfifig/all命令即可
Unix/linux系统:在终端窗口中使用ifconfifig或/sbin/ifconfifig命令即可
-
本地回环地址(hostAddress):127.0.0.1 主机名(hostName):localhost
端口号
-
IP地址 - 可以定位到具体某一台设备。
-
端口号 - 可以定位到该设备中具体某一个进程。
-
端口号本质上是16位二进制组成的整数,表示范围是:0 ~ 65535,其中0 ~ 1024之间的端口号通常被系统占用,建议编程从1025开始使用。
-
特殊的端口:
HTTP:80 FTP:21 Oracle:1521 MySQL:3306 Tomcat:8080
网络编程需要提供:IP地址 + 端口号,组合在一起叫做网络套接字:Socket。
基于TCP 的C/S 客户端/服务器模型
-
服务器:
(1)创建ServerSocket类型的对象并提供端口号;
(2)等待客户端的连接请求,调用accept()方法;
(3)使用输入输出流进行通信;
(4)关闭Socket;
-
客户端:
(1)创建Socket类型的对象并提供服务器的IP地址和端口号;
(2)使用输入输出流进行通信;
(3)关闭Socket;
-
ServerSocket类
java.net.ServerSocket类主要用于描述服务器套接字信息
// 1.创建ServerSocket类型的对象并提供端口号
ServerSocket ss = new ServerSocket(8888);
// 当没有客户端连接时,则服务器阻塞在accept方法的调用这里
// 侦听并接收到此套接字的连接请求
s = ss.accept();
// 用于关闭套接字
ss.close();
-
Socket类
java.net.Socket类主要用于描述客户端套接字,是两台机器间通信的端点
// 1.创建Socket类型的对象并提供服务器的主机名和端口号
Socket s = new Socket("127.0.0.1", 8888);
// InputStream getInputStream() 用于获取当前套接字的输入流
// OutputStream getOutputStream() 用于获取当前套接字的输出流
PrintStream ps = new PrintStream(s.getOutputStream());
BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
// void close() 用于关闭套接字
s.close();
客户端 Socket 与服务器端 Socket 对应, 都包含输入和输出流。
客户端的socket.getInputStream() 连接于服务器socket.getOutputStream()。
客户端的socket.getOutputStream()连接于服务器socket.getInputStream()
UDP模型
-
接收方:
(1)创建DatagramSocket类型的对象并提供端口号;
(2)创建DatagramPacket类型的对象并提供缓冲区;
(3)通过Socket接收数据内容存放到Packet中,调用receive方法;
(4)关闭Socket;
// 1.创建DatagramSocket类型的对象并提供端口号 ds = new DatagramSocket(8888); // 2.创建DatagramPacket类型的对象并提供缓冲区 byte[] bArr = new byte[20]; DatagramPacket dp = new DatagramPacket(bArr, bArr.length); // 3.通过Socket接收数据内容存放到Packet里面,调用receive方法 ds.receive(dp); // 实现将字符串内容"I received!"回发过去 byte[] bArr2 = "I received!".getBytes(); DatagramPacket dp2 = new DatagramPacket(bArr2, bArr2.length, dp.getAddress(), dp.getPort()); ds.send(dp2);
-
发送方:
(1)创建DatagramSocket类型的对象;
(2)创建DatagramPacket类型的对象并提供接收方的通信地址;
(3)通过Socket将Packet中的数据内容发送出去,调用send方法;
(4)关闭Socket;
// 1.创建DatagramSocket类型的对象
ds = new DatagramSocket();
// 2.创建DatagramPacket类型的对象并提供接收方的通信地址和端口号
byte[] bArr = "hello".getBytes();
DatagramPacket dp = new DatagramPacket(bArr, bArr.length, InetAddress.getLocalHost(), 8888);
// 3.通过Socket发送Packet,调用send方法
ds.send(dp);
System.out.println("发送数据成功!");
// 接收回发的数据内容
byte[] bArr2 = new byte[20];
DatagramPacket dp2 = new DatagramPacket(bArr2, bArr2.length);
ds.receive(dp2);
System.out.println("接收到的回发消息是:" + new String(bArr2, 0, dp2.getLength()));
URL类
- java.net.URL(Uniform Resource Identififier)类主要用于表示统一的资源定位器,也就是指向万维网上“资源”的指针。这个资源可以是简单的文件或目录,也可以是对复杂对象的引用,例如对数据库或搜索引擎的查询等。
- 通过URL可以访问万维网上的网络资源,最常见的就是www和ftp站点,浏览器通过解析给定的URL可以在网络上查找相应的资源。
- URL的基本结构如下:<传输协议>://<主机名>:<端口号>/<资源地址>
// 1.使用参数指定的字符串来构造对象
URL url = new URL("https://www.lagou.com/");
// 2.获取相关信息并打印出来
System.out.println("获取到的协议名称是:" + url.getProtocol());
System.out.println("获取到的主机名称是:" + url.getHost());
System.out.println("获取到的端口号是:" + url.getPort());
// 3.建立连接并读取相关信息打印出来
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
InputStream inputStream = urlConnection.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
String str = null;
while ((str = br.readLine()) != null) {
System.out.println(str);
}
br.close();
// 断开连接
urlConnection.disconnect();
反射
反射机制就是用于动态创建对象并且动态调用方法的机制
Class
- java.lang.Class类的实例可以用于描述Java应用程序中的类和接口,也就是一种数据类型。
- 该类没有公共构造方法,该类的实例由Java虚拟机和类加载器自动构造完成,本质上就是加载到内存中的运行时类。
- 使用数据类型.class的方式可以获取对应类型的Class对象(掌握)。
- 使用引用/对象.getClass()的方式可以获取对应类型的Class对象。
- 使用包装类.TYPE的方式可以获取对应基本数据类型的Class对象。
- 使用Class.forName()的方式来获取参数指定类型的Class对象(掌握)。
- 使用类加载器ClassLoader的方式获取指定类型的Class对象。
// 1.使用数据类型.class的方式可以获取对应类型的Class对象
Class c1 = String.class;
System.out.println("c1 = " + c1); // 自动调用toString方法 class java.lang.String
c1 = int.class;
System.out.println("c1 = " + c1); // int
c1 = void.class;
System.out.println("c1 = " + c1); // void
System.out.println("---------------------------------------------------");
// 2.使用对象.getClass()的方式获取对应的Class对象
String str1 = new String("hello");
c1 = str1.getClass();
System.out.println("c1 = " + c1); // class java.lang.String
Integer it1 = 20;
c1 = it1.getClass();
System.out.println("c1 = " + c1); // class java.lang.Integer
int num = 5;
//num.getClass(); Error: 基本数据类型的变量不能调用方法
System.out.println("---------------------------------------------------");
// 3.使用包装类.TYPE的方式来获取对应基本数据类型的Class对象
c1 = Integer.TYPE;
System.out.println("c1 = " + c1); // int
c1 = Integer.class;
System.out.println("c1 = " + c1); // class java.lang.Integer
System.out.println("---------------------------------------------------");
// 4.调用Class类中的forName方法来获取对应的Class对象
//c1 = Class.forName("String"); // Error 要求写完整的名称:包名.类名
c1 = Class.forName("java.lang.String");
System.out.println("c1 = " + c1); // class java.lang.String
c1 = Class.forName("java.util.Date");
System.out.println("c1 = " + c1); // class java.util.Date
//c1 = Class.forName("int");
//System.out.println("c1 = " + c1); // 不能获取基本数据类型的Class对象
System.out.println("---------------------------------------------------");
// 5.使用类加载器的方式来获取Class对象
ClassLoader classLoader = ClassTest.class.getClassLoader();
System.out.println("classLoader = " + classLoader); // null
c1 = classLoader.loadClass("java.lang.String");
System.out.println("c1 = " + c1); // class java.lang.String
// static Class<?> forName(String className) 用于获取参数指定类型对应的Class对象并返回
Class c1 = Class.forName("Person");
// T newInstance() 用于创建该Class对象所表示类的新实例
System.out.println("无参方式创建的对象是:" + c1.newInstance()); // null 0
Constructor类
java.lang.reflect.Constructor类主要用于描述获取到的构造方法信息
Class c1 = Class.forName("Person");
// 获取Class对象对应类中的无参构造方法,也就是Person类中的无参构造方法
Constructor constructor = c1.getConstructor();
// 使用获取到的无参构造方法来构造对应类型的对象,也就是Person类型的对象
System.out.println("无参方式创建的对象是:" + constructor.newInstance());
Class c1 = Class.forName("Person");
// 使用反射机制以有参方式构造Person类型的对象并打印
// 获取Class对象对应类中的有参构造方法,也就是Person类中的有参构造方法
Constructor constructor1 = c1.getConstructor(String.class, int.class);
// 使用获取到的有参构造方法来构造对应类型的对象,也就是Person类型的对象
// newInstance方法中的实参是用于给有参构造方法的形参进行初始化的,也就是给name和age进行初始化的
System.out.println("有参方式构造的对象是:" + constructor1.newInstance("zhangfei", 30));
// 使用反射机制获取Person类中所有的公共构造方法并打印
Constructor[] constructors = c1.getConstructors();
for (Constructor ct : constructors) {
System.out.println("构造方法的访问修饰符是:" + ct.getModifiers());
System.out.println("构造方法的方法名称是:" + ct.getName());
Class[] parameterTypes = ct.getParameterTypes();
System.out.print("构造方法的所有参数类型是:");
for (Class cs : parameterTypes) {
System.out.print(cs + " ");
}
System.out.println();
System.out.println("-------------------------------------------------");
}
Field类
java.lang.reflflect.Field类主要用于描述获取到的单个成员变量信息。
// 2.使用反射机制来构造对象以及获取成员变量的数值并打印
// 2.1 获取Class对象
Class c1 = Class.forName("Person");
// 2.2 根据Class对象获取对应的有参构造方法
Constructor constructor = c1.getConstructor(String.class, int.class);
// 2.3 使用有参构造方法来得到Person类型的对象
Object object = constructor.newInstance("zhangfei", 30);
// 2.4 根据Class对象获取对应的成员变量信息
Field field = c1.getDeclaredField("name");
// 设置Java语言访问检查的取消 暴力反射
field.setAccessible(true);
// 2.5 使用Person类型的对象来获取成员变量的数值并打印
// 获取对象object中名字为field成员变量的数值,也就是成员变量name的数值
System.out.println("获取到的成员变量数值为:" + field.get(object)); // zhangfei
System.out.println("-------------------------------------------------------");
// 4.使用反射机制修改指定对象中成员变量的数值后再次打印
// 表示修改对象object中名字为field成员变量的数值为guanyu,也就是成员变量name的数值为guanyu
field.set(object, "guanyu");
System.out.println("修改后成员变量的数值为:" + field.get(object)); // guanyu
System.out.println("-------------------------------------------------------");
// 5.获取Class对象对应类中所有的成员变量
Field[] declaredFields = c1.getDeclaredFields();
for (Field ft : declaredFields) {
System.out.println("获取到的访问修饰符为:" + ft.getModifiers());
System.out.println("获取到的数据类型为:" + ft.getType());
System.out.println("获取到的成员变量名称是:" + ft.getName());
System.out.println("---------------------------------------------");
}
Method类
java.lang.reflflect.Method类主要用于描述获取到的单个成员方法信息。
// 2.使用反射机制构造对象并调用方法打印结果
// 2.1 获取Class对象
Class c1 = Class.forName("Person");
// 2.2 根据Class对象来获取对应的有参构造方法
Constructor constructor = c1.getConstructor(String.class, int.class);
// 2.3 使用有参构造方法构造对象并记录
Object object = constructor.newInstance("zhangfei", 30);
// 2.4 根据Class对象来获取对应的成员方法
Method method = c1.getMethod("getName");
// 2.5 使用对象调用成员方法进行打印
// 表示使用object对象调用method表示的方法,也就是调用getName方法来获取姓名
System.out.println("调用方法的返回值是:" + method.invoke(object)); // zhangfei
System.out.println("------------------------------------------------------");
// 3.使用反射机制来获取类中的所有成员方法并打印
Method[] methods = c1.getMethods();
for (Method mt : methods) {
System.out.println("成员方法的修饰符是:" + mt.getModifiers());
System.out.println("成员方法的返回值类型是:" + mt.getReturnType());
System.out.println("成员方法的名称是:" + mt.getName());
System.out.println("成员方法形参列表的类型是:");
Class<?>[] parameterTypes = mt.getParameterTypes();
for (Class ct : parameterTypes) {
System.out.print(ct + " ");
}
System.out.println();
System.out.println("成员方法的异常类型列表是:");
Class<?>[] exceptionTypes = mt.getExceptionTypes();
for (Class ct: exceptionTypes) {
System.out.print(ct + " ");
}
System.out.println();
System.out.println("---------------------------------------------------");
}
其他信息
// 获取Student类型的Class对象
Class c1 = Class.forName("Student");
System.out.println("获取到的包信息是:" + c1.getPackage());
System.out.println("获取到的父类信息是:" + c1.getSuperclass());
System.out.println("-------------------------------------------------");
System.out.println("获取到的接口信息是:");
Class[] interfaces = c1.getInterfaces();
for (Class ct : interfaces) {
System.out.print(ct + " ");
}
System.out.println();
System.out.println("-------------------------------------------------");
System.out.println("获取到的注解信息是:");
Annotation[] annotations = c1.getAnnotations();
for (Annotation at : annotations) {
System.out.print(at + " ");
}
System.out.println();
System.out.println("-------------------------------------------------");
System.out.println("获取到的泛型信息是:");
Type[] genericInterfaces = c1.getGenericInterfaces();
for (Type tt : genericInterfaces) {
System.out.print(tt + " ");
}
System.out.println();