PrintStream类

  • 基本概念

    java.io.PrintStream类主要用于方便地打印各种数据并具有自动刷新的功能。

  • 常用的方法

    void print(String s) - 用于将参数指定的字符串内容打印出来.

    void println(String x) - 用于打印字符串后并终止该行.

    void close() - 用于关闭文件输出流并释放有关的资源.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import java.io.FileOutputStream;
import java.io.PrintStream;
public class PrintStreamTest {
public static void main(String[] args) {
try {
// 1.构造PrintStream类型的对象与c:/a.txt文件关联
PrintStream ps = new PrintStream(
new FileOutputStream("c:/a.txt"));
// 2.向文件中写入字符串内容"hello"
ps.println("hello");
//ps.print("hello");
System.out.println("写入字符串成功!");
// 3.关闭流对象并释放有关的资源
ps.close();
} catch(Exception e) {
e.printStackTrace();
}
}
}

BufferedReader类(重点)

  • 基本概念

    java.io.BufferedReader类主要用于从字符输入流读取单个字符、字符数组及字符串.

  • 常用的方法

    String readLine() - 用于读取一行字符串并返回

    void close() - 用于关闭文件输出流并释放有关的资源.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStreamReader;
public class BufferedReaderTest {
public static void main(String[] args) {
try {
// 1.构造BufferedReader类型的对象与c:/a.txt文件关联
BufferedReader br = new BufferedReader(
new InputStreamReader(new FileInputStream("c:/a.txt")));
// 2.从字符输入流中读取数据内容并打印出来
/*
int res = 0;
while((res = br.read()) != -1) {
// 当前系统默认的行分隔符是:\r\n - 1310
System.out.println("读取到的字符是:" + (char)res
+ ",对应的ASCII值:" + res);
}
*/
String str = br.readLine();
System.out.println("读取到的字符串是:" + str); // hello
// 3.关闭流对象并释放有关的资源
br.close();
} catch(Exception e) {
e.printStackTrace();
}
}
}

ObjectOutputStream类(重点)

  • 基本概念

    java.io.ObjectOutputStream类主要用于将Java语言的对象整体写入输出流中。

    只能将支持 java.io.Serializable 接口的对象写入流中。

    类通过实现 java.io.Serializable 接口以启用其序列化功能。

    所谓序列化是指将一个对象需要保存的所有相关信息有效组织成字节序列的转化过程

  • 常用的方法

    ObjectOutputStream(OutputStream out)
    根据参数指定引用来构造对象,其中OutputStream类是个抽象类,因此实参需要传递子类对象.

    void writeObject(Object obj) - 用于将参数指定的对象整体写入到输出流中.

    void close() - 用于关闭文件输出流并释放有关的资源.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
public class ObjectOutputStreamTest {
public static void main(String[] args) {
try {
// 1.构造ObjectOutputStream类型的对象与c:/a.txt文件关联
ObjectOutputStream oos = new ObjectOutputStream(
new FileOutputStream("c:/a.txt"));
// 2.准备User类型的对象并初始化
User u = new User("xiaomage", "123456", "13511253586");
// 3.将User类型的对象整体写入输出流中
oos.writeObject(u);
System.out.println("写入对象成功!");
// 4.关闭流对象并释放有关的资源
oos.close();
} catch(Exception e) {
e.printStackTrace();
}
}
}

ObjectInputStream类(重点)

  • 基本概念

    java.io.ObjectInputStream类主要用于从输入流中将Java语言的对象整体读取出来。

  • 常用方法

    ObjectInputStream(OutputStream out)
    根据参数指定引用来构造对象,其中InputStream类是个抽象类,因此实参需要传递子类对象.

    void readObject(Object obj)
    主要用于从输入流中读取一个对象并返回,无法通过返回值来判断是否读取到文件的末尾.

    void close() - 用于关闭文件输出流并释放有关的资源.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import java.io.FileInputStream;
import java.io.ObjectInputStream;
public class ObjectInputStreamTest {
public static void main(String[] args) {
try {
// 1.构造ObjectInputStream类型的对象与c:/a.txt文件关联
ObjectInputStream ois = new ObjectInputStream(
new FileInputStream("c:/a.txt"));
// 2.从输入流中读取一个对象并打印出来
User u = (User) ois.readObject(); //user是自己建的类.
System.out.println("读取到的对象是:" + u); // xiaomage 123456 ...
// 3.关闭流对象并释放有关的资源
ois.close();
} catch(Exception e) {
e.printStackTrace();
}
}
}
  • 经验分享:

    在以后的开发中若希望写入多个对象时,建议先将多个对象放入集合中,然后将集合看做一个对象整体调用一次writeObject方法写入。

    此时只需要调用一次readObject方法就可以将整个集合读取出来,从而避免了根据返回值进行文件末尾的判断。

线程

  • 基本概念

    程序 - 数据结构 + 算法,主要指存放在硬盘上的可执行文件。

    进程 - 主要指运行在内存中的程序。

    目前主流的操作系统都支持多进程,为了让操作系统同时执行多个任务从而提高效率,但进程是重量级的,新建进程对系统资源的消耗比较大,因此进程的数量比较局限。

    线程是进程内部的程序流,也就是操作系统中支持多进程,而每个进程的内部又可以支持多线程,并且线程是轻量级的,新建线程会共享所在进程的系统资源,因此以后的开发中都采用多线程技术。

    目前主流的多线程技术采用时间片轮转法实现多线程的并发执行,所谓并发就是宏观并行,微观串行的机制。

1
2
3
4
5
6
7
8
9
10
11
12
13
public class ThreadRunTest {
public static void main(String[] args) {
// 1.声明Thread类型的引用指向该类的对象
// 由源码可知:Thread类中的成员变量target最终的数值为 null。
Thread t1 = new Thread();
// 2.调用run方法进行测试
// 由run方法的源码可知:成员变量target != null不成立,则跳过{}中的代码
// 而run方法除了{}中的代码之外别无其他,因此证明run方法确实啥也不干
t1.run();
// 3.打印一句话
System.out.println("今天是个好日子!--- 愚人节!");
}
}

线程的创建(重中之重)

  • 线程创建和启动的方式

    java.lang.Thread类主要用于描述线程,Java虚拟机允许应用程序并发地运行多个执行线程,具体创建和启动方式如下:

    a.自定义类继承Thread类并重写run方法,创建该类的对象来调用start方法。

    b.自定义类实现Runnable接口并重写run方法,创建该类的对象作为实参构造Thread类型的对象,使用Thread类型的对象调用start方法。

  • 相关方法的解析

    Thread() - 使用无参的方式构造对象。

    Thread(Runnable target) - 根据参数指定的接口引用来构造对象。

    void run() - 若构造线程对象时使用Runnable对象,则调用该方法时最终调用Runnable对象指向的run方法;若构造该线程对象时没有使用Runnable对象,则调用该方法时啥也不做

    void start() - 用于启动线程,Java虚拟机会自动调用该线程的run方法。

  • 原理分析

    a.执行main方法的线程叫做主线程,而执行run方法的线程叫做子线程。

    b.程序启动时只有主线程来执行main方法中的代码,当start方法调用成功之后,线程的个数由1个瞬间变成了2个,而新启动的线程去执行run方法的代码,而执行main的线程继续向下执行,两个线程各自独立运行互不影响,当run方法执行完毕后子线程结束,而当main方法执行完毕后主线程结束。

    c.主线程和子线程的先后执行次序没有明确的规定,取决于系统的调度算法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class SubThreadStartTest extends Thread {
@Override
public void run() {
for(int i = 0; i < 20; i++) {
System.out.println("新线程中:i = " + i);
}
}

public static void main(String[] args) {
// 1.声明Thread类型的引用指向子类的对象,形成多态
Thread t1 = new SubThreadStartTest();
// 2.调用run方法和start方法
//t1.run(); // 普通成员方法的调用,根据方法名跳转过去执行完跳转回来
t1.start(); // 启动一个线程 两个线程同时执行

// 3.在main方法中打印整数
for(int i = 0; i < 20; i++) {
System.out.println("----------主线程中:i = " + i);
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class SubRunnableStartTest implements Runnable {
@Override
public void run() {
for(int i = 0; i < 20; i++) {
System.out.println("子线程中:i = " + i);
}
}

public static void main(String[] args) {
// 1.构造SubRunnableStartTest类型的对象
SubRunnableStartTest srst = new SubRunnableStartTest();
// 2.使用上述对象作为实参来构造Thread类型的对象
//srst.start(); error
// 由源码可知:Thread类中成员变量target的数值为 srst。
Thread t1 = new Thread(srst);
// 3.使用Thread类型的对象调用start方法
// Java虚拟机会自动调用该类的run方法,也就是调用Thread类的run方法
// 由run方法的源码可知:target != null 这个条件成立,执行
// target.run(); => srst.run()方法

// 思考:线程创建和启动的两种方式各有什么优缺点???
t1.start();

for(int i = 0; i < 20; i++) {
System.out.println("----------主线程中:i = " + i);
}
}
}
  • 注意:

    线程创建和启动的方式一相对代码简单,但Java语言中支持单继承,若该类继承Thread类后无法继承其他类,而线程创建和启动的方式二相对代码复杂,但该方式不影响该类继承其它类而且还可以多实现,因此以后的开发中推荐第二种方式。