这篇是本人JAVA基础学习的下篇,上篇链接在:
JAVA的学习心路历程之JDK基础入门(上)_Thomas_Lbw的博客-CSDN博客_jdk学习
目录
一、字符流
1.1 字符流基类
二、文件操作
2.1 文件操作
三、Swing
四、多线程编程
4.1 多线程概念
4.2 创建线程
一、字符流
字符流的相关内容在java.io包中。
1.1 字符流基类
java.io 包中专门用于字符流处理的类,是以 Reader 和 Writer 为基础派生的一系列类。字符流以字符为单位,根据码表映射字符,一次可能读多个字节,只能处理字符类型的数据。同类 InputStream 和 OutputStream 一样,Reader 和 Writer 也是抽象类,只提供了一系列用于字符流处理的接口。它们的方法与类 InputStream 和 OutputStream 类似,只不过其中的参数换成字符或字符数组。Reader 是所有的输入字符流的父类,它是一个抽象类。我们先来看一看基类 Reader 的方法,其用法与作用都与 InputStream 和 OutputStream 类似,就不做过多的说明了。
方法 | 返回值 |
---|---|
close() | void |
mark (int readAheadLimit) | void |
markSupported() | boolean |
read() | int |
read(char[] cbuf, int off,int len) | int |
ready() | boolean |
reset() | void |
skip(long n) | long |
Writer 是所有的输出字符流的父类,它是一个抽象类。 Writer 的方法:
方法 | 返回值 |
---|---|
close() | void |
flush() | void |
write(char[] cbuf) | void |
write(char[] cbuf, int off,int len) | void |
write(int c) | void |
write(String str) | void |
write(String str, int off, int len) | void |
InputStreamReader 和 OutputStreamWriter 是 java.io 包中用于处理字符流的最基本的类,用来在字节流和字符流之间作为中介:从字节输入流读入字节,并按编码规范转换为字符;往字节输出流写字符时先将字符按编码规范转换为字节。使用这两者进行字符处理时,在构造方法中应指定一定的平台规范,以便把以字节方式表示的流转换为特定平台上的字符表示。
InputStreamReader(InputStream in); //缺省规范说明
//指定规范 enc
InputStreamReader(InputStream in, String enc);
OutputStreamWriter(OutputStream out); //缺省规范说明
//指定规范 enc
OutputStreamWriter(OutputStream out, String enc);
如果读取的字符流不是来自本地时(比如网上某处与本地编码方式不同的机器),那么在构造字符输入流时就不能简单地使用缺省编码规范,而应该指定一种统一的编码规范“ISO 8859_1”,这是一种映射到 ASCCII 码的编码方式,能够在不同平台之间正确转换字符。
InputStreamReader ir = new InputStreamReader(is,"8859_1");
同样的,为了提高字符流处理的效率,java.io 中也提供了缓冲流 BufferedReader 和 BufferedWriter。其构造方法与 BufferedInputStream 和 BufferedOutPutStream 相类似。另外,除了 read()
和 write()
方法外,它还提供了整行字符处理方法:
- public String readLine():BufferedReader 的方法,从输入流中读取一行字符,行结束标志
\n
、\r
或者两者一起(这是根据系统而定的)。 - public void newLine():BufferedWriter 的方法,向输出流中写入一个行结束标志,它不是简单地换行符
\n
或\r
,而是系统定义的行隔离标志(line separator)。
eg:
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
public class FileToUnicode {
public static void main(String args[]) {
try {
FileInputStream fis = new FileInputStream("file1.txt");
InputStreamReader dis = new InputStreamReader(fis);
BufferedReader reader = new BufferedReader(dis);
String s;
//每次读取一行,当改行为空时结束
while((s = reader.readLine()) != null){
System.out.println("read:" + s);
}
dis.close();
}
catch(IOException e) {
System.out.println(e);
}
}
}
在这里我就列举一下有哪些类:
- 对字符数组进行处理: CharArrayReader、CharArrayWrite
- 对文本文件进行处理:FileReader、FileWriter
- 对字符串进行处理:StringReader、StringWriter
- 过滤字符流:FilterReader、FilterWriter
- 管道字符流:PipedReader、PipedWriter
- 行处理字符流:LineNumberReader
- 打印字符流:PrintWriter
类有千万,方法更是不计其数,所以没有必要去掌握所有的方法和类,只需要知道常见常用的就行了,而大多数的类和方法,希望大家有一个印象,当我们在实际开发的时候,能够想到,并且借助其他工具去查询我们需要的方法的应用方式就可以了。
二、文件操作
2.1 文件操作
java.io 定义的大多数类都是流式操作,但 File 类不是。它直接处理文件和文件系统。File 类没有指定信息怎样从文件读取或向文件存储;它描述了文件本身的属性。File 对象用来获取或处理与磁盘文件相关的信息,例如权限,时间,日期和目录路径。此外,File 还浏览子目录层次结构。Java 中的目录当成 File 对待,它具有附加的属性——一个可以被 list()
方法检测的文件名列表。
File 的构造方法:
//根据 parent 抽象路径名和 child 路径名字符串创建一个新 File 实例。
File(File parent, String child)
//通过将给定路径名字符串转换为抽象路径名来创建一个新 File 实例
File(String pathname)
// 根据 parent 路径名字符串和 child 路径名字符串创建一个新 File 实例
File(String parent, String child)
//通过将给定的 file: URI 转换为一个抽象路径名来创建一个新的 File 实例
File(URI uri)
eg:
//一个目录路径参数
File f1 = new File("/Users/mumutongxue/");
//对象有两个参数——路径和文件名
File f2 = new File("/Users/mumutongxue/","a.bat");
//指向 f1 文件的路径及文件名
File f3 = new File(f1,"a.bat");
再来看看 File 的一些方法:
方法 | 说明 |
---|---|
boolean canExecute() | 测试应用程序是否可以执行此抽象路径名表示的文件 |
boolean canRead() | 测试应用程序是否可以读取此抽象路径名表示的文件 |
boolean canWrite() | 测试应用程序是否可以修改此抽象路径名表示的文件 |
int compareTo(File pathname) | 按字母顺序比较两个抽象路径名 |
boolean createNewFile() | 当且仅当不存在具有此抽象路径名指定名称的文件时,不可分地创建一个新的空文件 |
static File createTempFile(String prefix, String suffix) | 在默认临时文件目录中创建一个空文件,使用给定前缀和后缀生成其名称 |
static File createTempFile(String prefix, String suffix, File directory) | 在指定目录中创建一个新的空文件,使用给定的前缀和后缀字符串生成其名称 |
boolean delete() | 删除此抽象路径名表示的文件或目录 |
void deleteOnExit() | 在虚拟机终止时,请求删除此抽象路径名表示的文件或目录 |
boolean equals(Object obj) | 测试此抽象路径名与给定对象是否相等 |
boolean exists() | 测试此抽象路径名表示的文件或目录是否存在 |
File getAbsoluteFile() | 返回此抽象路径名的绝对路径名形式 |
String getAbsolutePath() | 返回此抽象路径名的绝对路径名字符串 |
File getCanonicalFile() | 返回此抽象路径名的规范形式 |
String getCanonicalPath() | 返回此抽象路径名的规范路径名字符串 |
long getFreeSpace() | 返回此抽象路径名指定的分区中未分配的字节数 |
String getName() | 返回由此抽象路径名表示的文件或目录的名称 |
String getParent() | 返回此抽象路径名父目录的路径名字符串;如果此路径名没有指定父目录,则返回 null |
File getParentFile() | 返回此抽象路径名父目录的抽象路径名;如果此路径名没有指定父目录,则返回 null |
String getPath() | 将此抽象路径名转换为一个路径名字符串 |
long getTotalSpace() | 返回此抽象路径名指定的分区大小 |
long getUsableSpace() | 返回此抽象路径名指定的分区上可用于此虚拟机的字节数 |
int hashCode() | 计算此抽象路径名的哈希码 |
boolean isAbsolute() | 测试此抽象路径名是否为绝对路径名 |
boolean isDirectory() | 测试此抽象路径名表示的文件是否是一个目录 |
boolean isFile() | 测试此抽象路径名表示的文件是否是一个标准文件 |
boolean isHidden() | 测试此抽象路径名指定的文件是否是一个隐藏文件 |
long lastModified() | 返回此抽象路径名表示的文件最后一次被修改的时间 |
long length() | 返回由此抽象路径名表示的文件的长度 |
String[] list() | 返回一个字符串数组,这些字符串指定此抽象路径名表示的目录中的文件和目录 |
String[] list(FilenameFilter filter) | 返回一个字符串数组,这些字符串指定此抽象路径名表示的目录中满足指定过滤器的文件和目录 |
File[] listFiles() | 返回一个抽象路径名数组,这些路径名表示此抽象路径名表示的目录中的文件 |
File[] listFiles(FileFilter filter) | 返回抽象路径名数组,这些路径名表示此抽象路径名表示的目录中满足指定过滤器的文件和目录 |
File[] listFiles(FilenameFilter filter) | 返回抽象路径名数组,这些路径名表示此抽象路径名表示的目录中满足指定过滤器的文件和目录 |
static File[] listRoots() | 列出可用的文件系统根 |
boolean mkdir() | 创建此抽象路径名指定的目录 |
boolean mkdirs() | 创建此抽象路径名指定的目录,包括所有必需但不存在的父目录 |
boolean renameTo(File dest) | 重新命名此抽象路径名表示的文件 |
boolean setExecutable(boolean executable) | 设置此抽象路径名所有者执行权限的一个便捷方法 |
boolean setExecutable(boolean executable, boolean ownerOnly) | 设置此抽象路径名的所有者或所有用户的执行权限 |
boolean setLastModified(long time) | 设置此抽象路径名指定的文件或目录的最后一次修改时间 |
boolean setReadable(boolean readable) | 设置此抽象路径名所有者读权限的一个便捷方法 |
boolean setReadable(boolean readable, boolean ownerOnly) | 设置此抽象路径名的所有者或所有用户的读权限 |
boolean setReadOnly() | 标记此抽象路径名指定的文件或目录,从而只能对其进行读操作 |
boolean setWritable(boolean writable) | 设置此抽象路径名所有者写权限的一个便捷方法 |
boolean setWritable(boolean writable, boolean ownerOnly) | 设置此抽象路径名的所有者或所有用户的写权限 |
String toString() | 返回此抽象路径名的路径名字符串 |
URI toURI() | 构造一个表示此抽象路径名的 file: URI |
eg代码:
import java.io.File;
import java.io.IOException;
public class FileDemo {
public static void main(String[] args){
//同学们可以根据自己的路径进行更改
File f1 =new
File("/home/project/1.txt");
//File(String parent,String child)
File f2 =new File("/home/project","2.txt");
//separator 跨平台分隔符
File f3 =new File("/home"+File.separator+"project");
File f4 =new File(f3,"3.txt");
try {
System.out.println(f1);
//当文件存在时返回 false;不存在时返回 true
System.out.println(f2.createNewFile());
//当文件不存在时返回 false
System.out.println(f3.delete());
}catch(IOException e) {
e.printStackTrace();
}
//列出磁盘下的文件和文件夹
File[] files =File.listRoots();
for(File file:files){
System.out.println(file);
if(file.length()>0){
String[] filenames =file.list();
for(String filename:filenames){
System.out.println(filename);
}
}
}
}
}
对于 FileInputStream/FileOutputStream、FileReader/FileWriter 来说,它们的实例都是顺序访问流,即只能进行顺序读/写。而类 RandomAccessFile 则允许文件内容同时完成读和写操作,它直接继承 object,并且同时实现了接口 DataInput 和 DataOutput。
随机访问文件的行为类似存储在文件系统中的一个大型 byte 数组。存在指向该隐含数组的光标或索引,称为文件指针。输入操作从文件指针开始读取字节,并随着对字节的读取而前移此文件指针。如果随机访问文件以读取/写入模式创建,则输出操作也可用。输出操作从文件指针开始写入字节,并随着对字节的写入而前移此文件指针。
RandomAccessFile 提供了支持随机文件操作的方法:
- readXXX() 或者 writeXXX(): 如 ReadInt(),ReadLine(),WriteChar(),WriteDouble() 等
- int skipBytes(int n): 将指针向下移动若干字节
- length(): 返回文件长度
- long getFilePointer(): 返回指针当前位置
- void seek(long pos): 将指针调用所需位置
在生成一个随机文件对象时,除了要指明文件对象和文件名之外,还需要指明访问文件的模式。
我们来看看 RandomAccessFile 的构造方法:
RandomAccessFile(File file,String mode)
RandomAccessFile(String name,String mode)
eg:
import java.io.IOException;
import java.io.RandomAccessFile;
public class RandomFileDemo {
public static void main(String[] args){
int data_arr[] = {12, 32, 43, 45, 1, 5};
try {
RandomAccessFile randf=new RandomAccessFile("temp.dat","rw");
for(int i = 0; i < data_arr.length; i++){
randf.writeInt(data_arr[i]);
}
for(int i = data_arr.length-1 ; i >= 0; i--){
//int 数据占 4 个字节
randf.seek(i * 4L);
System.out.println(randf.readInt());
}
randf.close();
}catch(IOException e){
System.out.println("File access error" + e);
}
}
}
三、Swing
带你初步进入图形用户界面(GUI
)的世界,让你学会如何编写屏幕上那些具有特定大小和位置的窗体程序,并在其中添加文本,处理用户的输入。可以让你的程序真正地“有头有脸”——具有更好的人机交互性能。
下图表示了 MVC 组件类型的关系和功能。
四、多线程编程
4.1 多线程概念
作为一名开发者,你可能已经很熟悉操作系统中的多任务。这种在同一时刻同时运行多个程序的能力有效地提高了系统的利用率。
首先你应该知道什么是线程:
线程:程序执行流的最小单元。它是进程内一个相对独立的、可调度的执行单元,是系统独立调度和分派 CPU 的基本单位。
如同大自然中的万物,线程也有“生老病死”的过程,下图表示了一个线程从创建到消亡的过程,以及过程中的状态。
结合线程的生命周期,我们再来看看多线程的定义:
多线程:从软件或者硬件上实现多个线程并发执行的技术。在单个程序中同时运行多个线程完成不同的工作。
在 Java 中,垃圾回收机制就是通过一个线程在后台实现的,这样做的好处在于:开发者通常不需要为内存管理投入太多的精力。反映到我们现实生活中,在浏览网页时,浏览器能够同时下载多张图片。
从专业的角度来看,多线程编程是为了最大限度地利用 CPU 资源——当处理某个线程不需要占用 CPU 而只需要利用 IO 资源时,允许其他的那些需要 CPU 资源的线程有机会利用 CPU。这或许就是多线程编程的最终目的。当然,你也可以进一步了解 为什么使用多线程。
对于多线程和线程之间的关系,你可以这样理解:一个使用了多线程技术的程序,包含了两条或两条以上并发运行的线程(Thread
)。
Java 中的 Thread
类就是专门用来创建线程和操作线程的类,我们来具体学习一下。
4.2 创建线程
根据我们前面所学,我们可以自定义一个类,然后继承 Thread
类来使其成为一个线程类。
那么我们要把线程要做的事情放在哪里呢?在 Java 中,run()
方法为线程指明了它要完成的任务,你可以通过下面两种方式来为线程提供 run 方法:
- 继承 Thread 类并重写它的
run()
方法,然后用这个子类来创建对象并调用start()
方法。 - 通过定义一个类,实现 Runnable 接口,实现
run()
方法。
概括一下,启动线程的唯一的方法便是 start()
,而你需要把待完成的工作(功能代码)放入到 run()
方法中。
我们来创建两个线程试试。新建一个带有主方法的类 CreateThread
。
代码片段如下:
public class CreateThread {
public static void main(String[] args)
{
Thread1 thread1 = new Thread1();
//声明一个 Thread1 对象,这个 Thread1 类继承自 Thread 类的
Thread thread2 = new Thread(new Thread2());
//传递一个匿名对象作为参数
thread1.start();
thread2.start();
//启动线程
}
}
class Thread1 extends Thread
{
public void run()
{
//在 run() 方法中放入线程要完成的工作
//这里我们把两个线程各自的工作设置为打印 100 次信息
for (int i = 0; i < 100; ++i)
{
System.out.println("Hello! This is " + i);
}
//在这个循环结束后,线程便会自动结束
}
}
class Thread2 implements Runnable {
//与 Thread1 不同,如果当一个线程已经继承了另一个类时,就建议你通过实现 Runnable 接口来构造
public void run()
{
for (int i = 0; i < 100; ++i)
{
System.out.println("Thanks. There is " + i);
}
}
}
线程的状态共有 6 种,分别是:新建 New
、运行(可运行)Runnable
、阻塞 Blocked
、计时等待 Timed Waiting
、等待 Waiting
和终止 Terminate
。
当你声明一个线程对象时,线程处于新建状态,系统不会为它分配资源,它只是一个空的线程对象。 调用 start()
方法时,线程就成为了可运行状态,至于是否是运行状态,则要看系统的调度了。 调用了 sleep()
方法、调用 wait()
方法和 IO 阻塞时,线程处于等待、计时等待或阻塞状态。 当 run()
方法执行结束后,线程也就终止了。
我们通过一个例子来加深对于这些状态的理解。新建 ThreadState
类,用于自定义线程的状态。主要的代码如下:
public class ThreadState implements Runnable {
public synchronized void waitForAMoment() throws InterruptedException {
wait(500);
//使用 wait() 方法使当前线程等待 500 毫秒
//或者等待其他线程调用 notify() 或 notifyAll() 方法来唤醒
}
public synchronized void waitForever() throws InterruptedException {
wait();
//不填入时间就意味着使当前线程永久等待,
//只能等到其他线程调用 notify() 或 notifyAll() 方法才能唤醒
}
public synchronized void notifyNow() throws InterruptedException {
notify();
//使用 notify() 方法来唤醒那些因为调用了 wait() 方法而进入等待状态的线程
}
public void run() {
//这里用异常处理是为了防止可能的中断异常
//如果任何线程中断了当前线程,则抛出该异常
try {
waitForAMoment();
// 在新线程中运行 waitMoment() 方法
waitForever();
// 在新线程中运行 waitForever() 方法
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
然后再新建一个测试类 ThreadTest
,用于输出这些状态。
接下来会用到 sleep()
方法,下面给出了这个方法的使用方法。
sleep()
,在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。填入的参数为休眠的时间(单位:毫秒)。
调用代码:
public class ThreadTest {
public static void main(String[] args) throws InterruptedException {
ThreadState state = new ThreadState();
//声明并实例化一个 ThreadState 对象
Thread thread = new Thread(state);
//利用这个名为 state 的 ThreadState 对象来创建 Thread 对象
System.out.println("Create new thread: " + thread.getState());
//使用 getState() 方法来获得线程的状态,并进行输出
thread.start();
//使用 thread 对象的 start() 方法来启动新的线程
System.out.println("Start the thread: " + thread.getState());
//输出线程的状态
Thread.sleep(100);
//通过调用 sleep() 方法使当前这个线程休眠 100 毫秒,从而使新的线程运行 waitForAMoment() 方法
System.out.println("Waiting for a moment (time): " + thread.getState());
//输出线程的状态
Thread.sleep(1000);
//使当前这个线程休眠 1000 毫秒,从而使新的线程运行 waitForever() 方法
System.out.println("Waiting for a moment: " + thread.getState());
//输出线程的状态
state.notifyNow();
// 调用 state 的 notifyNow() 方法
System.out.println("Wake up the thread: " + thread.getState());
//输出线程的状态
Thread.sleep(1000);
//使当前线程休眠 1000 毫秒,使新线程结束
System.out.println("Terminate the thread: " + thread.getState());
//输出线程的状态
}
}
五、总结
JAVA的基础学习就到此为止吧。
阿根廷加油啊!梅西进球吧。