作者:老九—技术大黍
社交:知乎
公众号:老九学堂(新人有惊喜)
特别声明:原创不易,未经授权不得转载或抄袭,如需转载可联系笔者授权
前言
当我们读完了《Thinking in Java》之后,要进精进地学Java编程语言,那么一定要看《Core Java》一书。下面是我的读书笔记。
流
在Java API中,我们可以以字节序列读取的对象叫一个输入流(In the Java API, an object from which we can a sequence of bytes is called an input stream)。一个可以被字节序列被写的对象叫做输出流。而这些被操作的序列字节资源和目标常常是文件,也可能是网络连接和内存块。InputStream和OutputStream是抽象类,它们是I/O类的老祖宗。
因为字节流不能直接处理Unicode码存贮的信息,所以API单独把处理Unicode字符的类独立出来Reader和Writer两个抽象类。这两个灰是基于两个字节处理的,而不是一个字节处理的I/O流。
读取和写入字节
InputStream类一个抽象方法
abstract int read();
该方法一个字节一个字节的读取,然后一个字节一个字节的返回读取的内容,如果读完输入资源,那么返回-1。实现类必须重写该方法,以完成自己特定的功能。比如,FileInputStream类它从一个方法读字节。System.in是InputStream类的预定字类对象,它允许你从键盘读取信息。
InputStream类也有非抽象方法用来读取字节或者跳过字节的动作。这些访问会呼叫abstract read方法,所以子类只需要重写一个抽象方法即可。
不管是read和write方法它们在实际读取或者写入动作时,都会锁定(block)资源。这就意味着流对象不能立即被访问(一般为网络连接忙碌),或者当前线程锁定,结果会使其它线程等待流对象,并且该对象可用。available方法可以让我们检查当前读取字节数
int bytesAvailabe = in.available();
if(bytesAvailabe > 0){
byte[] data = new byte[bytesAvailabe];
in.read(data);
}
当我们读完流对象之后,需要呼叫close方法关闭流。
字节流对象
Java把流分为字节流和字符流。FileInputStream可以通过openStream方法从URL类得到流对象。PrintWriter和DataInputStream类可以把字符组合成有用的数据类型。Java程序可以整合使用这些流来操作文件。
ShowStreams类
package com.jb.arklis.demo;
import com.jb.arklis.zip.*;
import static java.lang.System.*;
import java.io.*;
import java.util.zip.*;
import java.util.*;
import com.jb.arklis.text.*;
import java.nio.charset.*;
import com.jb.arklis.random.*;
import com.jb.arklis.ser.*;
import com.jb.arklis.reg.*;
/**
功能:书写一个类用来演示流和文件的使用
作者:技术大黍
备注:
java.io包中的相对用户的工作目录(user's working directory),如果想知道当前的
工作目录,那么呼叫System.getProperty("user.dir");
*/
public class ShowStreams{
public ShowStreams(){
out.println(System.getProperty("user.dir"));
//demoStreamFilter();
//demoPushbackInputStream();
//demoReadZipFile();
//demoCharsetdemoPrintWriter();
//测试操作文本文档
//new TextFileOperationShow();
//demoCharset();
//demoRandomFileAccess();
//演示压缩流对象的使用
//new ZipFileFrame();
//new ObjectStreamTest();
//new RegExTest();
new HrefMatch();
}
//不管怎样修改文件内容都一个固定的双精度数值
private void demoStreamFilter(){
try{
FileInputStream fileInput = new FileInputStream("employee.dat");
//然后读入到内容
DataInputStream dataInput = new DataInputStream(fileInput);
//最后读成Java的数据内容
double salary = dataInput.readDouble();
out.println("当前薪水是:" + salary);
}catch(Exception e){
out.print(e.getMessage());
}
}
private void demoPushbackInputStream(){
try{
PushbackInputStream pushInput = new PushbackInputStream(
new BufferedInputStream(new FileInputStream("employee.dat")));
int b = pushInput.read();
if(b != '<'){
pushInput.unread(b);
out.println("当前b是:" + b);
}
}catch(Exception e){
out.print(e.getMessage());
}
}
private void demoReadZipFile(){
try{
ZipInputStream zipInput = new ZipInputStream(
new FileInputStream("employee.rar"));
DataInputStream dataInput = new DataInputStream(zipInput);
out.println("当前压缩档大小是:" + dataInput.readChar());
}catch(Exception e){
out.print(e.getMessage());
}
}
private void demoPrintWriter(){
try{
PrintWriter outInput = new PrintWriter("employee.dat","GBK");
String name = "Arklis 曾";
double salary = 75000.0;
outInput.print(name + ' ' + salary);
outInput.flush();
outInput.close();
}catch(Exception e){
out.println(e.getMessage());
}
}
//字符集是大小写敏感的
private void demoCharset(){
Charset charset = Charset.forName("ISO-8859-1");
//得到该字符集的别名
Set<String> aliases = charset.aliases();
for(String x : aliases){
out.println(x);
}
}
private void demoRandomFileAccess(){
new RandomwFileTest();
}
public static void main(String[] args){
new ShowStreams();
}
}
文本的输入与输出
当我们保存数据时,必须选择是使用二进制方式还是使用文本格式来保存。比如如果把整数1234保存为二进制形式,那么它的二进制字节序列是00 00 04 D2(16进制形式)。如果使用文本格式,那么它就是字符串“1234”格式。虽然二进制的I/O流的速度快,但是它的可读性不好。
当我们保存为文本字符串时,那么我们需要考虑字符编码问题(character encoding)。在UTF-16编码,字符串”1234”的编码是00 31 00 32 00 33 00 34(16进制方式),大多数程序都有不同的编码要求。ISO 8859-1编码是美国与西欧使用的试,这些“1234”会被编码为31 32 33 34没有0字节。
OutputStreamWriter类会把unicode字符转转换成字节流,转换时使用指定的编码方式对字符串进行编码。相对于InputSreamReader类会把不同的、指定的编码字符转换成unicode码字符。比如,我们使用如下命令:
InputStreamReader in = new InputStreamReader(System.in);
该命令是把控制输入的内容转换为unicode码字符。而下面命令是:
InputStreamReader in = new InputStreamReader(new FileInputStream(“kremli.dat”),”iso8859-1”);
它把.kremli.dat文档转换成iso8859-1编码格式的字符串。
TextFileOperationShow类
package com.jb.arklis.text;
import java.util.*;
import static java.lang.System.*;
import java.io.*;
import com.jb.arklis.random.*;
/*
测试的模式类
*/
public class Employee implements Serializable{
public static final int NAME_SIZE = 40;
public static final int RECORD_SIZE = 2 * NAME_SIZE + 8 + 4 + 4 + 4;
private String name;
private double salary;
private Date hireDay;
public Employee(){
}
public Employee(String name, double salary, int year, int month, int day){
this.name = name;
this.salary = salary;
GregorianCalendar calendar = new GregorianCalendar(year,month -1, day);
hireDay = calendar.getTime();
}
/**
加薪
*/
public void raiseSalary(double byPercent){
double raise = salary * byPercent / 100;
salary += raise;
}
public String toString(){
return getClass().getName() + "[name=" + name + ",salary="
+ salary + ",hireDay=" + hireDay + "]";
}
//员工执行输出持久化动作--把每个员的信息持久化文本文档中去
public void writeData(PrintWriter output){
GregorianCalendar calendar = new GregorianCalendar();
calendar.setTime(hireDay);
output.println(name + "|" + salary + "|" + calendar.get(Calendar.YEAR)
+ "|" + (calendar.get(Calendar.MONTH) + 1) + "|" +
calendar.get(Calendar.DAY_OF_MONTH));
}
//重载writeData方法
public void writeData(DataOutput output){
try{
//处理字符串比较特殊一些
DataIO.writeFixedString(name,NAME_SIZE, output);
//执行持久化输出动作
output.writeDouble(salary);
GregorianCalendar calendar = new GregorianCalendar();
calendar.setTime(hireDay);
//输出日期
output.writeInt(calendar.get(Calendar.YEAR));
output.writeInt(calendar.get(Calendar.MONTH) + 1);
output.writeInt(calendar.get(Calendar.DAY_OF_MONTH));
}catch(Exception e){
e.printStackTrace();
}
}
/**
从持久化文本文件中读取每个员工的信息
*/
public void readData(Scanner input){
String line = input.nextLine(); //读取每一行字符串
//out.println("当前持久文档中的第一行数据是:" + line);
String[] tokens = line.split("\\|"); //把|排出
name = tokens[0];
salary = Double.parseDouble(tokens[1]);
int year = Integer.parseInt(tokens[2]);
int month = Integer.parseInt(tokens[3]);
int day = Integer.parseInt(tokens[4]);
GregorianCalendar calendar = new GregorianCalendar(year,month - 1,day);
hireDay = calendar.getTime();
}
//重载readData()方法
public void readData(DataInput input)throws IOException{
//读取字符串比较特殊
name = DataIO.readFixedString(NAME_SIZE, input);
salary = input.readDouble();
//读取日期
int y = input.readInt();
int m = input.readInt();
int d = input.readInt();
GregorianCalendar calendar = new GregorianCalendar(y,m-1,d);
hireDay = calendar.getTime();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
public Date getHireDay() {
return hireDay;
}
public void setHireDay(Date hireDay) {
this.hireDay = hireDay;
}
}
Employee类
package com.jb.arklis.text;
import java.util.*;
import static java.lang.System.*;
import java.io.*;
import com.jb.arklis.random.*;
/*
测试的模式类
*/
public class Employee implements Serializable{
public static final int NAME_SIZE = 40;
public static final int RECORD_SIZE = 2 * NAME_SIZE + 8 + 4 + 4 + 4;
private String name;
private double salary;
private Date hireDay;
public Employee(){
}
public Employee(String name, double salary, int year, int month, int day){
this.name = name;
this.salary = salary;
GregorianCalendar calendar = new GregorianCalendar(year,month -1, day);
hireDay = calendar.getTime();
}
/**
加薪
*/
public void raiseSalary(double byPercent){
double raise = salary * byPercent / 100;
salary += raise;
}
public String toString(){
return getClass().getName() + "[name=" + name + ",salary="
+ salary + ",hireDay=" + hireDay + "]";
}
//员工执行输出持久化动作--把每个员的信息持久化文本文档中去
public void writeData(PrintWriter output){
GregorianCalendar calendar = new GregorianCalendar();
calendar.setTime(hireDay);
output.println(name + "|" + salary + "|" + calendar.get(Calendar.YEAR)
+ "|" + (calendar.get(Calendar.MONTH) + 1) + "|" +
calendar.get(Calendar.DAY_OF_MONTH));
}
//重载writeData方法
public void writeData(DataOutput output){
try{
//处理字符串比较特殊一些
DataIO.writeFixedString(name,NAME_SIZE, output);
//执行持久化输出动作
output.writeDouble(salary);
GregorianCalendar calendar = new GregorianCalendar();
calendar.setTime(hireDay);
//输出日期
output.writeInt(calendar.get(Calendar.YEAR));
output.writeInt(calendar.get(Calendar.MONTH) + 1);
output.writeInt(calendar.get(Calendar.DAY_OF_MONTH));
}catch(Exception e){
e.printStackTrace();
}
}
/**
从持久化文本文件中读取每个员工的信息
*/
public void readData(Scanner input){
String line = input.nextLine(); //读取每一行字符串
//out.println("当前持久文档中的第一行数据是:" + line);
String[] tokens = line.split("\\|"); //把|排出
name = tokens[0];
salary = Double.parseDouble(tokens[1]);
int year = Integer.parseInt(tokens[2]);
int month = Integer.parseInt(tokens[3]);
int day = Integer.parseInt(tokens[4]);
GregorianCalendar calendar = new GregorianCalendar(year,month - 1,day);
hireDay = calendar.getTime();
}
//重载readData()方法
public void readData(DataInput input)throws IOException{
//读取字符串比较特殊
name = DataIO.readFixedString(NAME_SIZE, input);
salary = input.readDouble();
//读取日期
int y = input.readInt();
int m = input.readInt();
int d = input.readInt();
GregorianCalendar calendar = new GregorianCalendar(y,m-1,d);
hireDay = calendar.getTime();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
public Date getHireDay() {
return hireDay;
}
public void setHireDay(Date hireDay) {
this.hireDay = hireDay;
}
}
写数据为二进制时使用DataOutputStream类;如果想写成文本格式时使用PrintWriter类来实现。那么,当我们需要读入数据到内存中,我们使用使用DataInputStream类来读二进制数据,在J2SE 5.0以后使用Scanner类来读文本字符,但是在J2SE 5.0以前都使用BufferedReader类来读取文本字符。
读取和写入二进制数据
DataOutput接口定义了写入数字、字符和boolean值为二进制格式的方法:
- writeChars
- writeByte
- writeInt
- writeShort
- writeLong
- writeFloat
- writeDouble
- writeChar
- writeBoolean
- writeUTF
比如writeInt方法总是会把一个整数写成四个字节的二进制数字,而不管数的进制;writeDouble总是会把双精度数写成八个字节的二进制数字。它们的输出没有阅读性,但是每个数据类型的所使用的空间是一样大小的,所以它们解析成文本时的速度很快。其中writeUTF方法把字符数据转换成八位的unicode编码格式。
RandomAccessFile类可以让我们在一个文件中随机查找或者写入数据。注意:该磁盘文件是可以随机访问的,但是来自网络流数据是不可以随机访问的。如果使用该类,那么可以实现对二者的只读和读/写操作,方式是通过在构造方法中使用“r”和”rw”字符串指定即可。
DataIO类
package com.jb.arklis.random;
import static java.lang.System.*;
import java.io.*;
/**
书写一个工具类,用来实现二进制字符集的读写操作
作者:技术大黍
*/
public class DataIO{
public static String readFixedString(int size, DataInput input)throws IOException{
StringBuilder message = new StringBuilder(size);
int i = 0;
boolean more = true;
while(more && i < size){
char ch = input.readChar();
i++;
if(ch == 0){//如果字符是结束符
more = false;//那么不读
}else{
//否则所字符添加到字符串中去
message.append(ch);
}
}
input.skipBytes(2 * (size - i)); //该语句是关键语句,该方法不抛出 EOFException异常
return message.toString();
}
//对比Employee类的writeData方法看看有何不同--一个是字符串,一个字符方式。
public static void writeFixedString(String temp, int size, DataOutput output)throws IOException{
for(int i = 0 ; i < size; i++){
char ch = 0;
//进行字符串的写动作
if(i < temp.length()){
//那么取出字符串的每个字符
ch = temp.charAt(i);
}
//输出字符
output.writeChar(ch);
}
}
}
RandomwFileTest类
package com.jb.arklis.random;
import static java.lang.System.*;
import com.jb.arklis.text.*;
import java.io.*;
import java.nio.charset.*;
/**
功能:书写一个随机访问文件类,用来演示通过字节集来进行读取和写入文件的动作
作者:技术大黍
备注:
我们使用二进制流处理字符集的方式实现文件的读取与写入动作。
*/
public class RandomwFileTest{
private Employee[] staff = new Employee[5];
public RandomwFileTest(){
staff[0] = new Employee("Carl Cracker", 75000, 1987, 12,15);
staff[1] = new Employee("Arklis Zeng", 75000, 1989, 10,1);
staff[2] = new Employee("Harray Hacker", 50000, 1990, 3,15);
staff[3] = new Employee("Tony Tester", 40000, 1991, 4,22);
staff[4] = new Employee("Huward Beast", 100000, 1988, 9,21);
try{
//指定输出的文件路径
DataOutputStream output = new DataOutputStream(
new FileOutputStream("./src/com/jb/arklis/random/employee.dat"));
for(Employee e: staff){
e.writeData(output);
}
output.close();
out.println("输出完毕!");
//下面获取数据,然后初始化对象数组
RandomAccessFile input = new RandomAccessFile("./src/com/jb/arklis/random/employee.dat","r");
//计算数据的大小
int n = (int)(input.length() / Employee.RECORD_SIZE);
Employee[] newStaff = new Employee[n];
//读取
for(int i = n - 1; i >= 0; i--){
newStaff[i] = new Employee();
//读取当前文件中每一行内容
input.seek(i * Employee.RECORD_SIZE);
newStaff[i].readData(input);
}
input.close();
//打印出来
for(Employee e: newStaff){
out.println(e);
}
}catch(Exception e){
e.printStackTrace();
}
}
}
压缩文件
ZIP压缩文档可以保存一个或者多个压缩格式的文件,每个ZIP文档都有一个头信息供压缩方法使用。我们可以通过使用ZipInputStream类读取通过ZipOutputStream类写成的压缩档。
ZipFileFrame类
package com.jb.arklis.zip;
import static java.lang.System.*;
import java.io.*;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.zip.*;
import java.util.*;
/**
功能:书写一个窗体类,用来演示怎样压缩文档
作者:技术大黍
*/
public class ZipFileFrame extends JFrame{
private JComboBox fileCombo;
private JTextArea fileText;
private String zipName;
public ZipFileFrame(){
setTitle("压缩流对象的操作演示");
Container container = getContentPane();
init(container);
setSize(400,300);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLocationRelativeTo(null);
setVisible(true);
zipFile();
}
private void zipFile(){
try{
FileOutputStream fout = new FileOutputStream("test.zip");
ZipOutputStream zout = new ZipOutputStream(fout);
ZipEntry ze = new ZipEntry("./src/com/jb/arklis/demo/ShowStreams.java");
zout.putNextEntry(ze);
zout.closeEntry();
ze = new ZipEntry("./src/com/jb/arklis/random/RandomwFileTest.java");
zout.putNextEntry(ze);
zout.closeEntry();
zout.close();
}catch(Exception e){
e.printStackTrace();
}
}
private void init(Container container){
//添加菜单对象
JMenuBar menuBar = new JMenuBar();
JMenu menu = new JMenu("文件");
JMenuItem openItem = new JMenuItem("打开");
menu.add(openItem); //添加打开菜单项
//处理打开文件事件
openItem.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent event){
//创建一个文件选择器对象,用来指定需要压缩的对象
JFileChooser chooser = new JFileChooser();
chooser.setCurrentDirectory(new File("."));//指定当前工作目录
//打开当前文档所在路径
int read = chooser.showOpenDialog(ZipFileFrame.this);
//如果可以添加到对话框
if(read == JFileChooser.APPROVE_OPTION){
//那么取得当前选中文件的路径
zipName = chooser.getSelectedFile().getPath();
fileCombo.removeAllItems();
//扫描压缩文档
scanZipFile();
}
}
});
JMenuItem exitItem = new JMenuItem("退出");
menu.add(exitItem);
exitItem.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent event){
exit(0);
}
});
menuBar.add(menu);
setJMenuBar(menuBar); //添加菜单条
//添加文本域和combo box
fileText = new JTextArea();
fileCombo = new JComboBox();
fileCombo.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent event){
//装载zip文件
loadZipFile((String)fileCombo.getSelectedItem());
}
});
container.add(fileCombo,BorderLayout.SOUTH);
container.add(new JScrollPane(fileText),BorderLayout.CENTER);
}
private void loadZipFile(final String name){
fileCombo.setEnabled(false);
fileText.setText("");
new SwingWorker<Void, Void>(){
protected Void doInBackground()throws Exception{
try{
//读取压缩文件
ZipInputStream zipInput = new ZipInputStream(new FileInputStream(zipName));
ZipEntry entry = zipInput.getNextEntry();
//找到匹配的压缩档
while(entry!= null){
if(entry.getName().equals(name)){
//那么读到内存中
//Scanner scanner = new Scanner(new FileReader(zipName));
String entryName = entry.getName();
File newFile = new File(entryName);
String directory = newFile.getParent();
//判断是否是一个文件
if(directory == null){
if(newFile.isDirectory()){
break;
}
}
//readByRandomAccessFile(entryName);//使用RandomAccessFile类读,中文有乱码问题
//readByFileReader(entryName); //正确显示不会有乱码问题
readByScanner(entryName); //使用scanner来读文件
}
zipInput.closeEntry(); //读下一压缩项
entry = zipInput.getNextEntry();
}
zipInput.close();
}catch(Exception e){
e.printStackTrace();
}
return null;
}
private void readByScanner(String entryName)throws IOException{
Scanner scanner = new Scanner(new FileReader(entryName));
while(scanner.hasNextLine()){
fileText.append(scanner.nextLine());
fileText.append("\n");
}
}
//使用BufferedReader不用担心文件的长度问题与文件指针移动问题
private void readByFileReader(String entryName)throws IOException{
FileReader fileReader = new FileReader(entryName);
BufferedReader bufferedReader = new BufferedReader(fileReader);
String line = bufferedReader.readLine();
while(line != null){
fileText.append(line);
fileText.append("\n");
line = bufferedReader.readLine();
}
fileReader.close();
}
private void readByRandomAccessFile(String entryName)throws IOException{
RandomAccessFile randomFile = new RandomAccessFile(entryName,"r");
String line;
while((line = randomFile.readLine()) != null){
fileText.append(line);
fileText.append("\n");
}
randomFile.close();
}
//重写done()方法
protected void done(){
fileCombo.setEnabled(true);
}
}.execute();
}
/**
扫描压缩档内容,并且生产combo box对象
*/
private void scanZipFile(){
new SwingWorker<Void, String>(){
protected Void doInBackground()throws Exception{
ZipInputStream zipInput = new ZipInputStream(new FileInputStream(zipName));
//声明一个压缩项
ZipEntry entry;
while((entry = zipInput.getNextEntry()) != null){//如果有数据项
//那么把资源发送到当前线程来处理
publish(entry.getName());
zipInput.closeEntry();
}
zipInput.close();
return null;
}
//重写process方法,用来处理压缩项
protected void process(java.util.List<String> names){
for(String x : names){
fileCombo.addItem(x); //combo组件添加项
}
}
}.execute();
}
}
运行效果
对象流和序列化
这张图表述了两个经理共用一个秘书。
内存与文件存贮的比较。
Manager类
package com.jb.arklis.ser;
import static java.lang.System.*;
import com.jb.arklis.text.*;
public class Manager extends Employee{
private Employee secretary;
public void setSecretary(Employee secretary){
this.secretary = secretary;
}
public Employee getSecretary(){
return secretary;
}
public Manager(){
}
public String toString(){
return super.toString() + "[secretary=" + secretary + "]";
}
public Manager(String name, double salary, int year, int month, int day){
super(name,salary,year,month,day);
secretary = null;
}
}
ObjectStreamTest类
package com.jb.arklis.ser;
import static java.lang.System.*;
import java.io.*;
import com.jb.arklis.text.*;
/**
功能:书写一个演示对象流的类
作者:技术大黍
*/
public class ObjectStreamTest{
private Employee[] staff = new Employee[3];
public ObjectStreamTest(){
Employee harry = new Employee("Harray Hacker", 50000, 1990, 3,15);
Manager arklis = new Manager("Arklis Zeng", 75000, 1989, 10,1);
arklis.setSecretary(harry);
Manager tony = new Manager("Tony Tester", 70000, 1991, 4,22);
tony.setSecretary(harry);
staff[0] = harry;
staff[1] = arklis;
staff[2] = tony;
//对数组执行序列化和反序列化
try{
ObjectOutputStream output = new ObjectOutputStream(new FileOutputStream("./src/com/jb/arklis/ser/employee.dat"));
output.writeObject(staff);
output.close();
//读取对象
ObjectInputStream input = new ObjectInputStream(new FileInputStream("./src/com/jb/arklis/ser/employee.dat"));
Employee[] newStaff = (Employee[])input.readObject();
input.close();
//给第二个员工加薪10%
newStaff[1].raiseSalary(10);
//显示出来
for(Employee e: newStaff){
out.println(e);
}
}catch(Exception e){
e.printStackTrace();
}
}
}
NIO
NIO包的导入是为了解决原IO包读/写速度的问题:
演示代码
package com.jb.arklis.io.nio;
import static java.lang.System.*;
import java.nio.*;
import java.nio.channels.*;
import java.io.*;
/**
功能:书写一个类用来演示文件锁的使用
作者:技术大黍
备注:
注意这里使用内存影射文件与文件锁的功能
*/
public class LockingMappedFiles{
static final int LENGTH = 0x4FFFF; //128MB 0x8FFFFFF
static FileChannel channel;
public static void showFileLock()throws IOException{
channel = new RandomAccessFile("testfilelock.dat","rw").getChannel();
//内存影射文件
MappedByteBuffer output = channel.map(FileChannel.MapMode.READ_WRITE, 0, LENGTH);
//使用内存影射文件对通道进行写操作
for(int i = 0; i < LENGTH; i++){
output.put((byte)'x');
}
//对通道进行锁定
new LockAndModify(output, 0, 0 + LENGTH / 3);
new LockAndModify(output, LENGTH / 2, LENGTH / 2 + LENGTH / 4);
}
//定义一个私有表态类,用来处理锁定义文件的动作
private static class LockAndModify extends Thread{
private ByteBuffer buffer;
private int start, end;
//在构造方法初始化成员变量
LockAndModify(ByteBuffer buffer, int start, int end){
this.start = start;
this.end = end;
buffer.limit(end);//指定缓存上限
buffer.position(start);//指定缓存起始位置
this.buffer = buffer.slice();
start(); //起动线程
}
//重写run()方法
public void run(){
try{
//执行锁定动作
FileLock lock = channel.lock(start, end, false);
//显示锁定结果
out.println("Locked:(锁定) " + start + " to(到) " + end);
//执行修改动作
while(buffer.position() < buffer.limit() - 1){
buffer.put((byte)(buffer.get() + 1));
}
//释放锁
lock.release();
out.println("Released:(释放) " + start + " to(到) " + end);
}catch(Exception e){
out.println(e.getMessage());
}
}
}
}
/**
功能:书写一个显示可用字节码来显示字符的类
作者:技术大黍
*/
public class GetDataFromByteBuffer{
private static final int BUFFER_SIZE = 1024;
public static void showCharFromByte(String test)throws IOException{
ByteBuffer buffer = ByteBuffer.allocate(BUFFER_SIZE);
//允许缓存自动增长
int i = 0;
while(i++ < buffer.limit()){
if(buffer.get() != 0){
print("不是零");
}
}
//否则打印i值
print("i = " + i);
buffer.rewind(); //输出
//然后存贮字符,接着显示它们
buffer.asCharBuffer().put(test);
char c;
while((c = buffer.getChar()) != 0){
printnb(c + " "); //把字符显示出来
}
print();
buffer.rewind();
//在缓存中存整数,然后显示出来
buffer.asShortBuffer().put((short)471143);
print(buffer.getShort());
buffer.rewind();
buffer.asIntBuffer().put(99471143);
print(buffer.getInt());
buffer.rewind();
buffer.asFloatBuffer().put(99471143);
print(buffer.getFloat());
buffer.rewind();
}
}
/**
功能:书写一个nio类,用来学nio包的使用
作者:技术大黍
*/
public class GetChannel{
//定义一个数据传送单位
private static final int BYTE_SIZE = 1024;
public static void useChannel(String filename){
try{
//使用channel来生成一个文件
FileChannel channel = new FileOutputStream(filename).getChannel();
//使用channel把向缓存中写内容
channel.write(ByteBuffer.wrap("测试数据字符(Test Data)\n".getBytes()));
channel.close();
//经今夜的添加一些内容
channel = new RandomAccessFile(filename,"rw").getChannel();
//把文件指针移动到尾部
channel.position(channel.size());
//写一些数据
channel.write(ByteBuffer.wrap("添加一些测试数据而已(Add some test data)。。。".getBytes()));
//关闭通道对象
channel.close();
//演示出来
channel = new FileInputStream(filename).getChannel();
//使用缓存来装载
ByteBuffer buffer = ByteBuffer.allocate(BYTE_SIZE);
//读取通道中去
channel.read(buffer);
//拉出数据
buffer.flip();
//如果缓存有
while(buffer.hasRemaining()){
//那么从里面合出来
out.print((char)buffer.get());
}
}catch(Exception e){
out.println(e.getMessage());
}
}
}
/**
功能:书写一个nio类,用来实现文件的拷贝功能
作者:技术大黍
*/
public class ChannelCopy{
//定义每次使用字节的单元
private static final int BUFFER_SIZE = 1024;
public static void copy(String source, String target)throws IOException{
//定义一个两个文件通道对象,用来完成拷贝的动作
FileChannel input = new FileInputStream(source).getChannel();
FileChannel output = new FileOutputStream(target).getChannel();
//声明一个ByteBuffer对象,用来具体执行拷贝的功能
ByteBuffer buffer = ByteBuffer.allocate(BUFFER_SIZE);
//搬东西--实现从源文件(source)拷贝到目标文件target中去
while(input.read(buffer) != -1){
buffer.flip(); //准备写入的动作
output.write(buffer);
buffer.clear(); //准备读取的动作
}
//以上循环可以使用下面语句代替
//input.transferTo(0,input.size(),output); //输入对象使用TO
//output.transferFrom(input, 0, input.size());//输出对象使用From
JOptionPane.showMessageDialog(null,"文件拷贝完毕!");
}
}
/**
功能:书写一个显示可用字符集的类
作者:技术大黍
*/
public class AvailableCharSets{
public void showCharSets(){
//使用Map来列字可用字符集
SortedMap<String,Charset> charSets = Charset.availableCharsets();
//使用迭代器取出现键
Iterator<String> iterator = charSets.keySet().iterator();
//循环显示集合中的内容
while(iterator.hasNext()){
String charSetName = iterator.next();
//打印出来
printnb(charSetName);
Iterator aliases = charSets.get(charSetName).aliases().iterator();
//如果有键
if(aliases.hasNext()){
printnb(": "); //打印冒号
}
//然后显示出值
while(aliases.hasNext()){
printnb(aliases.next());
//如果有多个值
if(aliases.hasNext()){
printnb(", ");//打印逗号
}
}
print();//换行
}
}
}
补充:Java正则表达式
正则表达式用来指定特定的字符串格式。比如
[Jj]ava.+
它表示匹配的字符串如下:
- 开头字母是J或者j
- 紧接着右边是三个字母ava
- 在ava右边是至少一个任意的字符
那么,”javaness”满足以上表达式,而”Core java”不满足以上表达式。下面我们来讲解正则表达式的语法:
1、大多数字符匹配自己,比如前面的ava 2、’.’符号表示匹配任意字符(除了一行的结束符) 3、使用’\’表示除外符,比如’.’表示一个点(.)而不是匹配任意字符,’\’表示匹配一个backslash 4、^和$分别表示一行字符的开始与结束处 5、如果X和Y是正则表达式,那么’XY’表示任意匹配X之后紧跟匹配Y,而’X|Y’表示任何匹配X的字符或者匹配Y的字符 6、可以使用数量来说明表达式,比如X+, X*或者X? 7、默认情况下,数字修饰符会以最大可能性去重复匹配,直到匹配成功。当使用后缀’?’修改表达式时,会以最少重复的方式来重复匹配;而使用‘+’时与之相反效果。比如,表达式[a-z]ab和[a-z]+ab之间的区别,前都[a-z]只匹配所有字符c,而ab只匹配ab字符;但是贪吃的[a-z]+表示字符集是cab,而剩下的ab不会再被匹配了。 8、使用’()’符号来进行子表达式的分组。比如([+-]?)([0-9]+),其中组号使用’\n’(比如’\1’)来指定分组的表达式。 测试: [+-]?[0-9]+|0[Xx][0-9A-Fa-f]+ 表达式表示的什么样的字符串?
在Java中,要求被匹配者必须是一个任意类对象,而该类必须实现CharSequence接口,比如String, StringBuilder, CharBuffer等。然后当我们编译正则表达式时,我们可使用六种标记来设置它。
Pattern pattern = Pattern.compile(patternString, Pattern.CASE_INSENSITIVE + Pattern.UNICODE_CASE);
六种标记如下:
如果正则表达式中有分组,那么Matcher对象可以获得分组的情况。组号0表示整个输入字符串内容;所以分组的实际第一个下标是1. 我们呼叫getCount方法得到所有分组数。
RegularTestUtil类
package com.jb.arklis.reg;
import static java.lang.System.*;
import java.util.regex.*;
import java.util.*;
import com.jb.arklis.io.nio.*;
/**
功能:书写一个测试正则表达式的工具类
作者:技术大黍
备注:输入的测试测试数据如下:
Input: "abcabcabcdefabc"
Regular expression: "abcabcabcdefabc"
Match "abcabcabcdefabc" at positions 0-14
Regular expression: "abc+"
Match "abc" at positions 0-2
Match "abc" at positions 3-5
Match "abc" at positions 6-8
Match "abc" at positions 12-14
Regular expression: "(abc)+"
Match "abcabcabc" at positions 0-8
Match "abc" at positions 12-14
Regular expression: "(abc){2,}"
Match "abcabcabc" at positions 0-8
*/
public class RegularTestUtil{
private String pattern;
private String content;
private Scanner scanner;
public RegularTestUtil(){
init(); //初始化成员变量
test(); //执行测试动作
}
private void init(){
scanner = new Scanner(System.in); //从键盘接收输入
out.println("输入正则表达式:");
//如果没有输入
pattern = scanner.nextLine();
if(pattern == null || pattern.trim().length() == 0){
out.println("请正则表达式和需要测试的数据^_^");
//那么结束程序
exit(0);
}
//否则执行测试
}
private void test(){
out.println("输入测试数据:");
content = scanner.nextLine();
//具体执行测试
Pattern testPattern = Pattern.compile(pattern);
Matcher matcher = testPattern.matcher(content);
//如果找到匹配对象
while(matcher.find()){
//那么打印出来
Print.print("匹配 \"" + matcher.group() +"\" 位置:"
+ matcher.start() + "-" + (matcher.end() - 1));
}
}
}
最后
记得给大黍❤️关注+点赞+收藏+评论+转发❤️
作者:老九学堂—技术大黍
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。