Java随笔(六)

与之前写的Java随笔一样,此篇博客主要是用来记录我之前存放在本地的Word文档中的一些Java的自身理解,由于水平有限所以可能只适合自己加深理解与记忆。


Java中大文件相关的读写

RandomAccessFile

如下,就是一个简单的使用RandomAccessFile实现的,将文件复制到另外地方。

import java.io.IOException;
import java.io.RandomAccessFile;
public class Test{
	public static void main(String[] args) throws IOException {
		try(RandomAccessFile file = new RandomAccessFile("D:\\a.txt","rw");
			RandomAccessFile fileB = new RandomAccessFile("D:\\b.txt","rw")
			){
			byte[] mm1 = new byte[(int) file.length()];
			file.read(mm1);
			fileB.write(mm1);
		}
	}
}

可以看到这里RandomAccessFile也是需要跟输入输出流一样进行关闭资源的,这里我们使用try-with-resource关闭资源。这里就是直接将a文件 全部读取到内存中,然后写入到fileB,看起来就跟我们正常的输入输出流差不多。

任意位置读写

下面演示使用RandomAccessFile对文件进行任意位置读取与追加。

假设一个文件a.txt,内容是123456789  而b.txt是一个有000000000内容的文件。

import java.io.IOException;
import java.io.RandomAccessFile;
public class TestFileChannel {
	public static void main(String[] args) throws IOException {
		
		try(RandomAccessFile fileA = new RandomAccessFile("D:\\a.txt","rw");
			RandomAccessFile fileB = new RandomAccessFile("D:\\b.txt","rw")){
			byte[] mm1 = new byte[(int) fileA.length()];
			fileA.read(mm1);
			fileB.seek(9);
			fileB.write(mm1);
		}
	}
}

上面代码 就是读取a.txt所有字节,然后 fileB.seek(9) 就是跳过9个字节,之后执行fileB.write()就会将 123456789写入到b.txt中。最后b.txt的文件内容就是000000000123456789。

如果fileA.read(mm1) 之前写入fileA.seek(4) 我们会发现最后b.txt中会写入56789以及4个空白字节(实际应该是字节0,因为我们声明的byte数组大小是a.txt的字节数,我们跳过4个字节然后写入到byte数组,所以有4个字节不会被设值)。

所以通过Seek方法我们就能实现 在任意位置进行文件的读取和写入。

Seek与skipBytes

另外我们还能使用fileB.skipBytes(9)实现上面的效果,skipBytes() 含义就是从当前位置跳过指定字节,如果就算得出的新的偏移位置大于文件长度,那么就将偏移位置设置为文件长度。下面是seek和skipBytes的源码说明。

seek的相关代码实现如下:

public void seek(long pos) throws IOException {
        if (pos < 0) {
            throw new IOException("Negative seek offset");
        } else {
            seek0(pos);
        }
    }

private native void seek0(long pos) throws IOException;

可以看到如果穿进去的参数不为负数,就调用了一个本地方法seek0,也就是这里真实操作是使用本地方法实现的。

而skipBytes相关代码如下:

public int skipBytes(int n) throws IOException {
        long pos;
        long len;
        long newpos;

        if (n <= 0) {
            return 0;
        }
        pos = getFilePointer();
        len = length();
        newpos = pos + n;
        if (newpos > len) {
            newpos = len;
        }
        seek(newpos);

        /* return the actual number of bytes skipped */
        return (int) (newpos - pos);
}
public native long getFilePointer() throws IOException;

我们可以看到在这里面也调用了seek方法进行位置的跳转。这里首先判断了传递的参数是否小于等于0,如果是 那么什么都不操作,直接返回0,而之后会调用getFilePointer 获取当前操作点的位置与文件开头的偏移量,(read、write、seek等方法 会影响这个getFilePointer返回的值),这里getFilePointer()也是一个本地方法。之后 用当前的偏移量与文件大小做对比,如果大于那么就将新的偏移量设置为文件大小,然后调用seek将当前偏移位置设置为新偏移位置,最后返回新旧位置差值。

Seek 可以将偏移位置设置为任意位置,skipBytes可以设置当前偏移位置向后跳过多少字节(如果新位置小于文件长度),skipBytes最大能到达文件末尾,skipBytes内部实际也是调用seek。

FileChannel

使用FileChannel可以更加容易 快速的进行文件复制。FileChannel减少了文件从内核态转为用户态(从Native方法的内存转到Java堆内存),因此快一些。

import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
public class TestFileChannel {
	public static void main(String[] args) throws IOException {
		
		try(RandomAccessFile fileA = new RandomAccessFile("D:\\a.txt","rw");
			RandomAccessFile fileB = new RandomAccessFile("D:\\b.txt","rw");
			FileChannel fileChannelA = fileA.getChannel();
			FileChannel fileChannelB = fileB.getChannel()){
			
			fileChannelB.transferFrom(fileChannelA, 0, fileChannelA.size());
			
		}
	}
}

如上面 就是直接使用fileChannelB.transFrom 从fileChannelA中复制信息到b.txt,或者使用fileChannelA.transTo,以及使用如下通过将ByteBuffer作为中间对象实现复制。

ByteBuffer buffer = ByteBuffer.allocate((int) fileChannelA.size());
fileChannelA.read(buffer);
buffer.flip();
fileChannelB.write(buffer,0);

对RandomAccessFile做seek等操作,也能影响fileChannelB.write的位置。

或者可以使用FileChannel.open(Paths.get("D:\\a.txt"),StandardOpenOption.READ, StandardOpenOption.WRITE)  来得到FileChannel。然后操作FileChannel。

MappedByteBuffer

我们可以通过FileChannel对象的map方法,得到文件的区域位置映射。

如MappedByteBuffer mappedByteBufferA = fileChannelA.map(MapMode.READ_WRITE, 0, fileChannelA.size());得到可读可写的MappedByteBuffer。我们通过对这个可读可写的MappedByteBuffer操作,从而来影响文件数据。

public class TestFileChannel {
	public static void main(String[] args) throws IOException {
		
		try(RandomAccessFile fileA = new RandomAccessFile("D:\\a.txt","rw");
			RandomAccessFile fileB = new RandomAccessFile("D:\\b.txt","rw");
			FileChannel fileChannelA = FileChannel.open(Paths.get("D:\\a.txt"),
StandardOpenOption.READ, StandardOpenOption.WRITE);
			FileChannel fileChannelB = fileB.getChannel()){
			MappedByteBuffer mappedByteBufferA = fileChannelA.map(
MapMode.READ_ONLY, 0, fileChannelA.size());
			MappedByteBuffer mappedByteBufferB = fileChannelB.map(
MapMode.READ_WRITE, 0, fileChannelA.size());
			mappedByteBufferB.put(mappedByteBufferA);
			mappedByteBufferB.force();
		}
	}
}

如上面的操作就会将a.txt的数据复制到b.txt,我们可以认为MappedByteBuffer就是文件在Java对象的一个映射,操作这个对象,就能影响文件内容。

FileChannel提供了map方法来把文件映射为MappedByteBuffer: MappedByteBuffer map(int mode,long position,long size); 可以把文件的从position开始的size大小的区域映射为MappedByteBuffer,mode指出了可访问该内存映像文件的方式,共有三种,分别为:

MapMode.READ_ONLY(只读): 试图修改得到的缓冲区将导致抛出 ReadOnlyBufferException。

MapMode.READ_WRITE(读/写): 对得到的缓冲区的更改最终将写入文件;但该更改对映射到同一文件的其他程序不一定是可见的(无处不在的“一致性问题”又出现了)。

MapMode.PRIVATE(专用): 可读可写,但是修改的内容不会写入文件,只是buffer自身的改变,这种能力称之为”copy on write”。

再简单的说一下,MappedByteBuffer较之ByteBuffer新增的三个方法:

fore()缓冲区是READ_WRITE模式下,此方法对缓冲区内容的修改强行写入文件。

load()将缓冲区的内容载入内存,并返回该缓冲区的引用。

isLoaded()如果缓冲区的内容在物理内存中,则返回真,否则返回假

Java中Debug追踪源码,无法查看局部变量解决方法

在java中,我们使用Debug追踪到源码,发现Debug中无法查看局部变量的值,这造成了一定的困扰。这里出现这个问题的原因在于oracle提供的jre中rt.jar不带debug信息:orcale在编译src时使用了 javac -g:none,意思是不带任何调试信息,这样可以减小rt.jar的大小。若想正常调试jdk,就只能重新编译src.zip。这里介绍下编译src.zip的方法。

首先我们在eclipse里面建立一个Java项目,名为jdk,然后在src目录上导入"Archive File",选择对应jdk下的源码src.zip导入。

然后我们在项目上右键导出选择JAR file,导出的jar包名设置为rt_debug.jar。

最后修改我们eclipse里面对应的jre设置,将rt_debug.jar添加到JRE system libraries里面,并将其设置到最前面,这样我们在使用这个jre进行debug的时候就能看到局部变量的值了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值