一、内存文件映射


内存文件映射允许我们创建和修改那些因为太大而不能放入内存中的文件。有了内存文件映射,我们就可以假定整个文件都在内存中,而且可以完全把文件当作数组来访问。

package com.dy.xidian;

import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel; public class LargeMappedFiles {
static int length = 0x8FFFFFF;
public static void main(String[] args) throws Exception {
@SuppressWarnings("resource")
MappedByteBuffer out = new RandomAccessFile("test.dat", "rw")
.getChannel().map(FileChannel.MapMode.READ_WRITE, 0, length);
for (int i = 0; i < length; i++)
out.put((byte) 'x');
System.out.println("Finished writing");
for (int i = length / 2; i < length / 2 + 6; i++)
System.out.print((char) out.get(i));
}
}

map中的0表示从文件的起始位置开始映射,映射的大小为length(128MB)。MappedByteBuffer是直接缓冲区(JVM对直接缓冲区的内容会直接进行IO操作,如果缓冲区是非直接的,JVM会先将用户缓冲区的数据copy到自己的缓冲区,在对自己的缓冲区进行IO操作),

所以MappedByteBuffer对大文件的操作速度要比普通的IO流快。MappedByteBuffer继承自ByteBuffer,所以我们对ByteBuffer的操作都适用于ByteBuffer。

二、对映射文件的部分加锁


  文件映射通常应用于极大的文件。我们可能需要对这种巨大的文件进行部分加锁,以便其它进程可以修改文件中未被加锁的部分。例如:数据库就是这样吗,因此多个用户可以同时访问它

package com.dy.xidian;

import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock; public class LockingMappedFiles {
static final int LENGTH = 0X8FFFFFF;
static FileChannel fc;
@SuppressWarnings("resource")
public static void main(String[] args) throws Exception {
fc = new RandomAccessFile("test.dat", "rw").getChannel();
MappedByteBuffer out = fc
.map(FileChannel.MapMode.READ_WRITE, 0, LENGTH);
for (int i = 0; i < LENGTH; i++)
out.put((byte) 'x');
new LockAndModify(out, 0, LENGTH / 3);
new LockAndModify(out, LENGTH / 2, LENGTH / 2 + LENGTH / 4);
}
private static class LockAndModify extends Thread {
private ByteBuffer buff;
private int start, end; LockAndModify(ByteBuffer buff, int start, int end) {
super();
this.start = start;
this.end = end;
this.buff = buff;
buff.limit(end);
buff.position(start);
//创建一个新缓冲区标识对象
//该对象的position=0,limit和capacity等于原缓冲区剩余字节数。
buff = buff.slice();
start();
} public void run() {
try {
// 获取通道上的锁,指定加锁的起始位置和结束位置
// 将锁设置为独占锁
FileLock fl = fc.lock(start, end, false);
System.out.println("Locked: " + start + " to " + end);
while (buff.position() < buff.limit() - 1)
buff.put((byte) (buff.get() + 1));
fl.release();
System.out.println("Released: " + start + " to " + end);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}

3.GZIP的使用范例


package com.dy.xidian;

import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream; public class GZIPcompress {
public static void main(String[] args) throws IOException {
if (args.length == 0) {
System.out.println("Usage: \n\tGZIPcompress file\n"
+ "\tUses GZIP compress to compress "
+ "the file to test.gz");
System.exit(1);
}
BufferedReader in = new BufferedReader(new FileReader(args[0]));
BufferedOutputStream out = new BufferedOutputStream(
new GZIPOutputStream(new FileOutputStream("test.gz")));
System.out.println("Writing file");
int c;
while ((c = in.read()) != -1) {
out.write(c);
}
in.close();
out.close();
System.out.println("Reading file");
BufferedReader in2 = new BufferedReader(new InputStreamReader(
new GZIPInputStream(new FileInputStream("test.gz"))));
String s;
while ((s = in2.readLine()) != null)
System.out.println(s);
in2.close();
}
}

4.对象序列化


  对象的序列化就是将那些实现了Seriallizable接口的对象转换成一个字节序列,并能够在以后将这个字节序列完全恢复为原来的对象。而且这种方式可以跨操作系统实现对象的传输,可以在window上将对象序列化,并通过网络传输给linux操作系统,然后在linux系统上进行对象的还原。对象的序列化可以实现轻量级持久性。"持久性"意味着一个对象的生存周期并不取决于程序是否正在执行,它可以存在于程序的调用之间。通过序列化将一个对象对象写入磁盘,然后在重新调用程序时恢复该对象。对象必须在程序中显示地序列化和反序列化还原。

  • 序列化实例

  对象的序列化首先要创建某些OutputStream对象,然后将其封装在一个ObjectOutpuStream对象中。这时,只需要调用writeObject()即可将对象序列化,并将其发送给OutputStream(对象的序列化是基于子节的,因要使用InputStream和OutputStream)。要反向进行该过程,需要将一个InputStream封装在ObjectInputStream内,然后调用readObject()。和往常一样,我们最后获得个是一个引用,它指向一个向上转型的Object(),所以必须向下转型才能设置他们。

package com.dy.xidian;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.Random; @SuppressWarnings("serial")
// Serializble是一个空接口,作为标识来表明该对象可以序列化
class Data implements Serializable {
private int n; public Data(int n) {
super();
this.n = n;
}
public String toString() {
return Integer.toString(n);
}
} @SuppressWarnings("serial")
public class Worm implements Serializable {
private static Random rand = new Random(47);
private Data[] d = {new Data(rand.nextInt(10)), new Data(rand.nextInt(10)),
new Data(rand.nextInt(10))}; private Worm next;
private char c;
public Worm(int i, char x) {
System.out.println("Worm constructor: " + i);
c = x;
// 递归创建对象,每个对象中的next指向下一个对象
if (--i > 0)
next = new Worm(i, (char) (x + 1));
} public Worm() {
System.out.println("Default constructor");
} public String toString() {
StringBuilder result = new StringBuilder(":");
result.append(c);
result.append("(");
for (Data dat : d) {
result.append(dat);
}
result.append(")");
// 递归打印对象信息
if (next != null)
result.append(next);
return result.toString();
} public static void main(String[] args) throws Exception {
Worm w = new Worm(6, 'a');
System.out.println("w = " + w);
// 对基本流进行包装,将对象写到文件中
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(
"worm.out"));
out.writeObject("Worm storage\n");
out.writeObject(w);
out.close();
// 从文件中读取对象
ObjectInputStream in = new ObjectInputStream(new FileInputStream(
"worm.out"));
String s = (String) in.readObject();
Worm w2 = (Worm) in.readObject();
if (w2 instanceof Worm)
System.out.println(s + "w2 = " + w2);
in.close(); //将对象序列化写到内存中
ByteArrayOutputStream bout = new ByteArrayOutputStream();
ObjectOutputStream out2 = new ObjectOutputStream(bout);
out2.writeObject("Worm storage\n");
out2.writeObject(w);
out2.flush();
out2.close();
//创建一个新的byteArray数组
//bout存储区的数据复制到新数组中
ObjectInputStream in2 = new ObjectInputStream(new ByteArrayInputStream(
bout.toByteArray()));
s = (String) in2.readObject();
Worm w3 = (Worm) in2.readObject();
System.out.println(s + "w3 = " + w3);
in2.close();
}
}
  • 寻找类

  反序列化在生成对象时不是凭空的,它需要参考该对象的类文件(class),然后通过类文件才能还原该对象(因为反序列化底层应用了反射机制)。所以类文件应该放在classpath路径下。

  • 序列化的控制

  有时我们考虑到安全问题,不希望一个的对象的所有部分都被序列化;或者是,一个对象被还原后,某子对象需要重新创建,从而不必将该对象序列化。在这种特殊情况下,可以通过实现Externalizable接口来替代Serializable接口。Extertnalizable接口继承了Serializable接口,同时增添了两个方法:writeExternal()和readExternal()。这个方法会在序列化和反序列化还原的过程中被自动调用,以便执行一些特殊的操作。注意: 一旦继承Externalizable接口,意味着对象的所有属性都不会再自动序列化。

package com.dy.xidian;

import java.io.Externalizable;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream; public class Blip implements Externalizable {
private int i;
private String s;
public Blip() {
System.out.println("Blip Constructor");
} public Blip(String x, int a) {
System.out.println("Blip(String x, int a)");
s = x;
i = a;
} public String toString() {
return s + i;
} @Override
public void writeExternal(ObjectOutput out) throws IOException {
System.out.println("Blip.writeExternal");
out.writeObject(s);
out.writeInt(i);
} @Override
public void readExternal(ObjectInput in) throws IOException,
ClassNotFoundException {
System.out.println("Blip.readExternal");
s = (String) in.readObject();
i = in.readInt();
} public static void main(String[] args) throws IOException,
ClassNotFoundException {
System.out.println("Constructing objects: ");
Blip b = new Blip("A String", 47);
System.out.println(b);
ObjectOutputStream o = new ObjectOutputStream(new FileOutputStream(
"Blip.out"));
System.out.println("Saving object");
o.writeObject(b);
o.close();
ObjectInputStream in = new ObjectInputStream(new FileInputStream(
"Blip.out"));
System.out.println("Recovering b:");
b = (Blip) in.readObject();
System.out.println(b);
in.close();
}
}

我们可以在writeExternal()和readExternal()来指定,将对象中那些属性进行序列化和反序列化。

Externalizable与Serializable的区别:Serializable是从存储的二进制数据中直接恢复出对象的,它不会调用构造器。而对于一个Externalizable对象,则默认的构造器都会被调用,然后再调用readExternal()。

  transient(瞬时)关键字

  该关键字使得我们在使用serializable时,可以让敏感属性不被序列化。

package com.dy.xidian;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.Date;
import java.util.concurrent.TimeUnit; public class Login implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
private Date date = new Date();
private String username;
private transient String password;
public Login(String name, String pwd) {
username = name;
password = pwd;
} public String toString() {
return "logon info:\n username: " + username + "\n date: " + date
+ "\n password: " + password;
} public static void main(String[] args) throws IOException,
InterruptedException, ClassNotFoundException {
Login a = new Login("dy", "111111");
System.out.println("login a = " + a);
ObjectOutputStream o = new ObjectOutputStream(new FileOutputStream(
"Login.out"));
o.writeObject(a);
o.close();
TimeUnit.SECONDS.sleep(1);
ObjectInputStream in = new ObjectInputStream(new FileInputStream(
"Login.out"));
System.out.println("Recovering object at " + new Date());
a = (Login) in.readObject();
System.out.println("Login a = " + a);
in.close();
}
}

  当我们将同一个对象在同一个流进行多次序列化后(对同一个对象多次调用writeObject方法),然后在同一流上对该对象多次反序列化后(多次调用readObject方法),那么反序列化后,内存中只会存在一个该对象,这很正常,因为在序列化时该对象也只有一个(即使我们序列化了多次)。但是,我们如果我们使用不同的流对该对象进行反序列话,那么内存会产生两个这样的对象。原因在于流之间是不会检测所要反序列化的对象是否存在于对方的流中。可以这样来记忆,每使用一个新的流进行反序列化时,就好像在new一个对象,使用同一流来反序列化同一种对象时,就好像在进行引用赋值。

package com.dy.xidian;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.Serializable; class Horse implements Serializable{}
class Animal implements Serializable{
private Horse h;
private String name; public Animal(String nm, Horse h){
name = nm;
this.h = h;
} public String toString(){
return name + super.toString() + this.h;
}
} public class MyWorld {
public static void main(String[] args) throws IOException {
Horse h = new Horse();
Animal n1 = new Animal("a", h);
ByteArrayOutputStream buffer1 = new ByteArrayOutputStream();
ObjectOutputStream o1 = new ObjectOutputStream(buffer1);
o1.writeObject(n1);
o1.writeObject(n1);
ByteArrayOutputStream buffer2 = new ByteArrayOutputStream();
ObjectOutputStream o2 = new ObjectOutputStream(buffer1);
o1.writeObject(n1);
}
} #

5.受限数据集存储


Preferences类提供了对于小的数据集的存储方式,它对于数据集的存储的方式和操作系统(比如在windows操作系统上会被存储到windows的注册表中)相关,我们不必了解具体的存储细节,主要关注一下该API如何使用

package cn.xdysite;
public class Test {
public static void main(String[] args) {
Preferences prefs = Preferences.userNodeForPackage(Test.class);
prefs.put("name", "Bob");
prefs.putInt("Bob", 123456789);
prefs.putBoolean("single", true);
int a = prefs.getInt("count", 0);
System.out.println(a);
prefs.putInt("count", ++a);
}
}

Preferences会根据传入的参数(必须是Class对象,这里是Test.class)创建一个存储节点,然后将数据存储的到节点,我们每运行一次上面的代码,会发现输出的值会比上一次大1.对于存储节点的区分是根据包来区分的,同一个包下的class对象会生成同一个存储节点,比如cn.xdysite下有个Test1和Test2类,那么我们传入Test1.class作为存储节点,可以用Test1.clTest2.class来取数据

7.参考文章

序列化安全问题:http://my.oschina.net/u/1188877/blog/529611?p={{page}}

JAVA中的NIO(二)的更多相关文章

  1. Java中的NIO基础知识

    上一篇介绍了五种NIO模型,本篇将介绍Java中的NIO类库,为学习netty做好铺垫 Java NIO 由3个核心组成,分别是Channels,Buffers,Selectors.本文主要介绍着三个 ...

  2. JAVA中的NIO (New IO)

    简介 标准的IO是基于字节流和字符流进行操作的,而JAVA中的NIO是基于Channel和Buffer进行操作的. 传统IO graph TB; 字节流 --> InputStream; 字节流 ...

  3. java中的NIO和IO到底是什么区别?20个问题告诉你答案

    摘要:NIO即New IO,这个库是在JDK1.4中才引入的.NIO和IO有相同的作用和目的,但实现方式不同,NIO主要用到的是块,所以NIO的效率要比IO高很多. 本文分享自华为云社区<jav ...

  4. java中封装类(二)

    java中的数字类型包括 Byte,Short,Integer,Long,Float,Double.其中前四个是整数,后两个是浮点数. 在说java中数字类型之前先来看看它们统一的基类Number. ...

  5. Java中的NIO及IO

    1.概述 Java NIO(New IO) 是从Java 1.4版本开始引入的一个新的IO API,可以替代标准的Java IO API.NIO与原来的IO有同样的作用和目的,但是使用的方式完全不同, ...

  6. Java中的NIO和IO的对比分析

    总的来说,java中的IO和NIO主要有三点区别: IO NIO 面向流 面向缓冲 阻塞IO 非阻塞IO 无 选择器(Selectors) 1.面向流与面向缓冲 Java NIO和IO之间第一个最大的 ...

  7. JAVA中的NIO(一)

    1.IO与NIO IO就是普通的IO,或者说原生的IO.特点:阻塞式.内部无缓冲,面向流. NIO就是NEW IO,比原生的IO要高效.特点:非阻塞.内部有缓存,面向缓冲. 要实现高效的IO操作,尤其 ...

  8. JAVA 中BIO,NIO,AIO的理解

    [转自]http://qindongliang.iteye.com/blog/2018539 ?????????????????????在高性能的IO体系设计中,有几个名词概念常常会使我们感到迷惑不解 ...

  9. java中的IO二

    java中流分为节点流和处理流,IO一中的案例都是节点流 一.处理流的使用实例 二.装饰者模式 以上BufferReader的用法就是装饰者模式 Decorator就是动态地给对象增添行为 如果要实现 ...

随机推荐

  1. [转载]UEditor报错TypeError: me.body is undefined

    本文转载来自:UEditor报错TypeError: me.body is undefined 今天在使用UEditor的setContent的时候报错,报错代码如下 TypeError: me.bo ...

  2. 解决在iOS8环境下,当用户关闭定位服务总开关时,无法将APP定位子选项加入定位权限列表的问题

    关键点:- (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizati ...

  3. js控制iframe跳转

    网页:<iframe src="" id="iframe_a" name="iframe_a" style="width:1 ...

  4. ubuntu自带的gedit编辑器添加Markdown预览插件

    gedit安装Markdown Preview Ubuntu自带的gedit编辑器也是有很强大的功能的,且支持插件的安装.对于喜欢用Markdown的我来说,这当然是很好的了,gedit本身 就支持M ...

  5. Hive UDF’S addMonths

    our project use hive 0.10 , and in the hiveql , we need use addMonths function builtin in hive-0.11. ...

  6. MIT jos 6.828 Fall 2014 训练记录(lab 3)

    注:源代码参见我的github: https://github.com/YaoZengzeng/jos Part A : User Environments and Exception Handlin ...

  7. 使用javascript获取url中的参数

    方法一: //取url参数 var type = request("type") function request() { var query = location.search; ...

  8. 介绍linux下利用编译bash设置root账号共用的权限审计设置

    在日常运维工作中,公司不同人员(一般是运维人员)共用root账号登录linux服务器进行维护管理,在不健全的账户权限审计制度下,一旦出现问题,就很难找出源头,甚是麻烦!在此,介绍下利用编译bash使不 ...

  9. zepto.js 源码解析

    http://www.runoob.com/w3cnote/zepto-js-source-analysis.html Zepto是一个轻量级的针对现代高级浏览器的JavaScript库, 它与jqu ...

  10. 12Spring_AOP编程(AspectJ)_前置通知

    接下里的博客会一篇一篇的讲解每一个通知.其实AOP_AspectJ的编程与传统的AOP的编程的最大的区别就是写一个Aspect 支持多个Advice和多个PointCut .而且我们写AOP_Aspc ...