русс | укр

Языки программирования

ПаскальСиАссемблерJavaMatlabPhpHtmlJavaScriptCSSC#DelphiТурбо Пролог

Компьютерные сетиСистемное программное обеспечениеИнформационные технологииПрограммирование

Все о программировании


Linux Unix Алгоритмические языки Аналоговые и гибридные вычислительные устройства Архитектура микроконтроллеров Введение в разработку распределенных информационных систем Введение в численные методы Дискретная математика Информационное обслуживание пользователей Информация и моделирование в управлении производством Компьютерная графика Математическое и компьютерное моделирование Моделирование Нейрокомпьютеры Проектирование программ диагностики компьютерных систем и сетей Проектирование системных программ Системы счисления Теория статистики Теория оптимизации Уроки AutoCAD 3D Уроки базы данных Access Уроки Orcad Цифровые автоматы Шпаргалки по компьютеру Шпаргалки по программированию Экспертные системы Элементы теории информации

Блокирование части отображаемого файла


Дата добавления: 2015-06-12; просмотров: 828; Нарушение авторских прав


Как уже было упомянуто, отображение файлов обычно используется для файлов очень больших размеров. Иногда при работе с таким большим файлом требуется заблокировать некоторые его части, в то время как доступные части будут изменяться другими процессами. В частности, такой подход характерен для баз данных, чтобы несколько пользователей могли работать с базой одновременно. В следующем примере каждый их двух потоков блокирует свою собственную часть файла:

//: io/LockingMappedFiles.java

// Блокирование части отображаемого файла.

// {RunByHand}

import java.nio.*;

import java.nio.channels.*;

import java.io.*;

 

public class LockingMappedFiles {

static final int LENGTH = 0x8FFFFFF; // 128 MB

static FileChannel fc;

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, 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 mbb, int start, int end) {

this.start = start;

this.end = end;

mbb.limit(end);

mbb.position(start);

buff = mbb.slice();

start();

}

public void run() {

try {

// Exclusive lock with no overlap:

// Монопольная блокировка без перекрытия:

FileLock fl = fc.lock(start, end, false);

System.out.println("Locked: "+ start +" to "+ end);

// Perform modification:

// Модификация:

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);

}

}

}

}

Класс потока LockAndModify устанавливает область буфера и получает его для модификации методом slice(). В методе run() для файлового канала устанавливается блокировка (вы не вправе запросить блокировку для буфера, это позволено только для канала). Вызов lock()напоминает механизм синхронизации доступа потоков к объектам, у вас появляется некая «критическая секция» с монопольным доступом к данной части файла. Блокировки автоматически снимаются при завершении работы JVM, закрытии канала, для которого они были получены, но можно также явно вызвать метод release() объекта FileLock, что здесь и показано.

Сжатие данных

Библиотека ввода/вывода Java содержит классы, поддерживающие ввод/вывод в сжатом формате (табл. 16.8). Они базируюся на уже существующих потоках ввода/вывода.

Эти классы не являются частью иерархии символьно-ориентированных потоков Reader и Writer, они надстроены над байт-ориентированными классами InputStream и OutputStream, так как библиотека сжатия работает не с символами, а с байтами. Впрочем, никто не запрещает смешивать потоки. (Помните, как легко преобразовать потоки из байтовых в символьные — достаточно использовать классы InputStreamReader и OutputStreamWriter.)

Хотя существует великое количество различных программ сжатия данных, форматы ZIP и GZIP используются, пожалуй, чаще всего. Таким образом, вы можете легко манипулировать своими сжатыми данными с помощью многочисленных программ, предназначенных для чтения и записи этих форматов.

 

Простое сжатие в формате GZIP

Интерфейс сжатия данных в формате GZIP является наиболее простым и идеально подходит для ситуаций, где имеется один поток данных, который необходимо уплотнить (а не разрозненные фрагменты данных). В следующем примере сжимается файл:

//: io/GZIPcompress.java

// {Args: GZIPcompress.java}

import java.util.zip.*;

import java.io.*;

 

public class GZIPcompress {

public static void main(String[] args)

throws IOException {

if(args.length == 0) {

System.out.println(

"Usage: \nGZIPcompress file\n" +

"\tUses GZIP compression 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);

}

} /* (Execute to see output) *///:~

Работать с классами сжатия данных очень просто: вы просто надстраиваете их для своего потока данных (GZIPOutputStream или ZipOutputStream для сжатия, GZIPInputStream или ZipInputStream для распаковки данных). Дальнейшее сводится к элементарным операциям ввода/вывода. В примере продемонстрированы смешанные байтовые и символьные потоки: поток in основан на Reader, тогда как конструктор класса GZIPOutputStream использует только потоки на основе OutputStream, но не Writer. Поэтому при открытии файла потокGZIPInputStream преобразуется в символьный поток Reader.

 

Многофайловые архивы ZIP

Библиотека, поддерживающая формат сжатия данных ZIP, обладает гораздо более широкими возможностями. С ее помощью можно легко упаковывать произвольное количество файлов, а для чтения файлов в формате ZIP даже определен отдельный класс. В библиотеке поддержан стандартный ZIP-формат, поэтому сжатые ею данные будут восприниматься практически любым упаковщиком. Структура следующего примера совпадает со структурой предыдущего, но количество файлов, указываемых в командной строке, не ограничено. Вдобавок демонстрируется применение класса Checksum для получения и проверки контрольной суммы. Таких типов контрольных сумм в Java два: один представлен классом Adler32(этот алгоритм быстрее), а другой — классом CRC32 (медленнее, но точнее).

//: io/ZipCompress.java

// Использование формата ZIP для сжатия любого

// количества файлов, указанных в командной строке.

// {Параметры. ZipCompress java}

import java.util.zip.*;

import java.io.*;

import java.util.*;

import static net.mindview.util.Print.*;

 

public class ZipCompress {

public static void main(String[] args)

throws IOException {

FileOutputStream f = new FileOutputStream("test.zip");

CheckedOutputStream csum =

new CheckedOutputStream(f, new Adler32());

ZipOutputStream zos = new ZipOutputStream(csum);

BufferedOutputStream out =

new BufferedOutputStream(zos);

zos.setComment("A test of Java Zipping");

// No corresponding getComment(), though.

for(String arg : args) {

print("Writing file " + arg);

BufferedReader in =

new BufferedReader(new FileReader(arg));

zos.putNextEntry(new ZipEntry(arg));

int c;

while((c = in.read()) != -1)

out.write(c);

in.close();

out.flush();

}

out.close();

// Checksum valid only after the file has been closed!

print("Checksum: " + csum.getChecksum().getValue());

// Now extract the files:

print("Reading file");

FileInputStream fi = new FileInputStream("test.zip");

CheckedInputStream csumi =

new CheckedInputStream(fi, new Adler32());

ZipInputStream in2 = new ZipInputStream(csumi);

BufferedInputStream bis = new BufferedInputStream(in2);

ZipEntry ze;

while((ze = in2.getNextEntry()) != null) {

print("Reading file " + ze);

int x;

while((x = bis.read()) != -1)

System.out.write(x);

}

if(args.length == 1)

print("Checksum: " + csumi.getChecksum().getValue());

bis.close();

// Alternative way to open and read Zip files:

ZipFile zf = new ZipFile("test.zip");

Enumeration e = zf.entries();

while(e.hasMoreElements()) {

ZipEntry ze2 = (ZipEntry)e.nextElement();

print("File: " + ze2);

// ... and extract the data as before

}

/* if(args.length == 1) */

}

} /* (Execute to see output) *///:~

Для каждого файла, добавляемого в архив, необходимо вызвать метод putNextEntry() с соответствующим объектом ZipEntry. Класс ZipEntry содержит все необходимое для добавления к отдельной записи ZIP-файла дополнительной информации: имени файла, размера в сжатом и обычном виде, контрольной суммы CRC, дополнительных данных, комментариев, метода сжатия, признака каталога. В исходном формате ZIP также можно задать пароли, но библиотека Java не поддерживает эту возможность. Аналогичное ограничение встречается и при использовании контрольных сумм: потоки CheckedInputStream и CheckedOutputStream поддерживают оба вида контрольных сумм — и Adler32, и CRC32, однако в классе ZipEntry поддерживается только CRC. Это ограничение вынужденное, поскольку продиктовано требованиями формата ZIP, однако при этом быстрая контрольная сумма Adler32 оказывается в неравных условиях с CRC.

Для извлечения файлов в классе ZipInputStream предусмотрен метод getNextEntry(), который возвращает очередной элемент архива ZipEntry. Для получения более компактной записи можно использовать для архива объект ZipFile, чей ме­тод entries() возвращает итераторEnumeration, с помощью которого можно перемещаться по доступным элементам архивного файла.

Чтобы Иметь доступ к контрольной сумме, необходимо каким-либо образом хранить представляющий ее объект Checksum. В нашем случае сохраняются ссылки на потоки CheckedInputStream и CheckedOutputStream, хотя можно было бы просто сохранить ссылки на объекты Checksum.

Неясно, зачем в библиотеку сжатия ZIP был добавлен метод setComment(), вставляющий в архивный файл комментарий. Как указано в примере, добавить комментарий при получении архивного файла можно, но восстановить его при чтении архива потоком ZipInputStreamнельзя. Полноценные комментарии поддерживаются только для отдельных вхождений ZIP-архива, объектов ZipEntry.

Конечно, уплотняемые данные не ограничены файлами; пользуясь библиотеками ZIP и GZIP, вы можете сжимать все, что угодно, даже данные сетевых потоков.

 

Архивы Java ARchives (файлы JAR)

Формат ZIP также применяется в файлах JAR (архивы Java ARchive), предназначенных для упаковки группы файлов в один сжатый файл. Как и все в языке Java, файлы JAR являются кросс-платформенными, поэтому не нужно заботиться о совместимости платформ. Наравне с файлами классов, в них могут содержаться также любые файлы — например, графические и мультимедийные.

Файлы JAR особенно полезны при работе с Интернетом. До их появления веб-браузерам приходилось выдавать отдельный запрос к серверу для каждого файла, необходимого для запуска апплета. Вдобавок все эти файлы не сжимались. Объединение всех нужных для запуска апплета файлов в одном сжатом файле сокращает время запроса к серверу, при этом уменьшается и загрузка сервера. Кроме того, каждый элемент архива JAR можно снабдить цифровой подписью.

Файл JAR представляет собой файл, в котором хранится набор сжатых файлов вместе с манифестом (manifest), который их описывает. (Вы можете создать манифест самостоятельно или же поручить эту работу программе jar.) За подробной информацией о манифестах JARобращайтесь к документации JDK.

Инструмент jar, который поставляется вместе с пакетом разработки программ JDK, автоматически сжимает файлы по вашему выбору. Запускается эта программа из командной строки:

jar [параметры] место_назначения [манифест] список_файлов

Параметры запуска — просто набор букв (дефисы или другие служебные символы не нужны). Пользователи систем UNIX/Linux сразу заметят сходство с программой tar. Допустимы следующие параметры:

с - Создание нового или пустого архива

t - Вывод содержимого архива

х - Извлечение всех файлов

х - файл Извлечение файла с заданным именем

f - Признак имени файла. Если не использовать этот параметр, jar решит, что входные

данные поступают из стандартного ввода, или при создании файла выходные данные будут

направляться в стандартный поток вывода

m - Означает, что первый аргумент содержит имя файла, содержащего манифест

v - Выводит краткое описание действий, выполняемых программой jar

о - Сохранение файлов без сжатия (для создания файлов JAR, которые можно указать

в переменной окружения CLASSPATH)

М - Отказ от автоматического создания манифеста

Если в списке файлов имеется каталог, то его содержимое вместе с подкаталогами и всеми файлами автоматически помещается в файл JAR. Информация о пути файлов также сохраняется.

Несколько примеров наиболее распространенных вариантов запуска программы jar:

jar cf myJarFile.jar *.class

Команда создает файл JAR с именем myJarFile.jar, в котором содержатся все файлы классов из текущего каталога, с автоматически созданным манифестом:

jar cmf myJarFile.jar myManifestFile.mf *.class

Почти идентична предыдущей команде, за одним исключением — в полученный файл JAR включается пользовательский манифест из файла myManifestFile.mf:

jar tf myJarFile.jar

Вывод содержимого (списка файлов) архива myJarFile.jar:

jar tvf myJarFile.jar

К предыдущей команде добавлен параметр v для получения более подробной информации о файлах, содержащихся в архиве myJarFile.jar:

jar cvf myApp.jar audio classes image


Предполагается, что audio, classes и image — это каталоги, содержимое которых включается в файл myApp.jar. Благодаря параметру v в процессе сжатия выводится дополнительная информация об упаковываемых файлах. Инструмент jar не обладает возможностями архиватора zip. Например, он не позволяет добавлять или обновлять файлы в уже существующем архиве JAR. Также нельзя перемещать файлы и удалять их после перемещения. Но при этом созданный файл JAR всегда читается инструментом jar на другой платформе (архиваторы zip о такой совместимости могут только мечтать).

Сериализация объектов

Сериализация (serialization) объектов Java позволяет вам взять любой объект, реализующий интерфейс Serializable, и превратить его в последовательность байтов, из которой затем можно полностью восстановить исходный объект. Сказанное справедливо и для сетевых соединений, а это значит, что механизм сериализации автоматически компенсирует различия между операционными системами. То есть можно создать объект на машине с ОС Windows, превратить его в последовательность байтов, а затем послать их по сети на машину с ОС UNIX, где объект будет корректно воссоздан. Вам не надо думать о различных форматах данных, порядке следования байтов или других деталях.

Сама по себе сериализация объектов интересна потому, что с ее помощью можно осуществить легковесное долговременное хранение (lightweight persistence). Вспомните: это означает, что время жизни объекта определяется не только вре­менем выполнения программы — объект существует и между запусками программы. Можно взять объект и записать его на диск, а после, при другом запуске программы, восстановить его в первоначальном виде и таким образом получить эффект «живучести». Причина использования добавки «легковесное» такова: объект нельзя определить как «постоянный» при помощи некоторого ключевого слова, то есть долговременное хранение напрямую не поддерживается языком (хотя вероятно, такая возможность появится в будущем). Система выполнения не заботится о деталях сериализации — вам приходится собственноручно сериализовывать и восстанавливать объекты вашей программы. Если вам необходим более серьезный механизм сериализации, попробуйте библиотеку Java JDO или инструмент, подобный Hibernate(http://hibernate.sourceforge.net).

Механизм сериализации объектов был добавлен в язык для поддержки двух расширенных возможностей. Удаленный вызов методов Java (RMI) позволяет работать с объектами, находящимися на других компьютерах, точно так же, как и с теми, что существуют на вашей машине. При посылке сообщений удаленным объектам необходимо транспортировать аргументы и возвращаемые значения, а для этого используется сериализация объектов.

Сериализация объектов также необходима визуальным компонентам JavaBean. Информация о состоянии визуальных компонентов обычно изменяется во время разработки. Эту информацию о состоянии необходимо сохранить, а затем, при запуске программы, восстановить; данную задачу решает сериализация объектов.

Сериализовать объект достаточно просто, если он реализует интерфейс Serializable (это интерфейс для самоидентификации, в нем нет ни одного метода). Когда в язык был добавлен механизм сериализации, во многие классы стандартной библиотеки внесли изменения так, чтобы они были готовы к сериализации. К таким классам относятся все классы-оболочки для простейших типов, все классы контейнеров и многие другие. Даже объекты Class, представляющие классы, можно сериализовать.

Чтобы сериализовать объект, требуется создать выходной поток OutputStream, который нужно вложить в объект ObjectOutputStream. По сути, вызов метода writeObject() осуществляет сериализацию объекта, и далее вы пересылаете его в выходной поток данныхOutputStream. Для восстановления объекта необходимо надстроить объект ObjectInputStream для входного потока InputStream, а затем вызвать метод readObject(). Как обычно, такой метод возвращает ссылку на обобщенный объект Object, поэтому после вызова метода следует провести нисходящее преобразование для получения объекта нужного типа.

Сериализация объектов проводится достаточно разумно и в отношении ссылок, имеющихся в объекте. Сохраняется не только сам образ объекта, но и все связанные с ним объекты, все объекты в связанных объектах, и т. д. Это часто называют «паутиной объектов», к которой можно присоединить одиночный объект, а также массив ссылок на объекты и объекты-члены. Если бы вы создавали свой собственный механизм сериализации, отслеживание всех присутствующих в объектах ссылок стало бы весьма нелегкой задачей. Однако в Java никаких трудностей со ссылками нет — судя по всему, в этот язык встроен достаточно эффективный алгоритм создания графов объектов. Следующий пример проверяет механизм сериализации: мы создаем цепочку связанных объектов, каждый из которых связан со следующим сегментом цепочки, а также имеет массив ссылок на объекты другого класса с именем Data:

//: io/Worm.java

// Тест сериализации объектов.

import java.io.*;

import java.util.*;

import static net.mindview.util.Print.*;

 

class Data implements Serializable {

private int n;

public Data(int n) { this.n = n; }

public String toString() { return Integer.toString(n); }

}

 

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;

// Value of i == number of segments

public Worm(int i, char x) {

print("Worm constructor: " + i);

c = x;

if(--i > 0)

next = new Worm(i, (char)(x + 1));

}

public Worm() {

print("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 ClassNotFoundException, IOException {

Worm w = new Worm(6, 'a');

print("w = " + w);

ObjectOutputStream out = new ObjectOutputStream(

new FileOutputStream("worm.out"));

out.writeObject("Worm storage\n");

out.writeObject(w);

out.close(); // Also flushes output

ObjectInputStream in = new ObjectInputStream(

new FileInputStream("worm.out"));

String s = (String)in.readObject();

Worm w2 = (Worm)in.readObject();

print(s + "w2 = " + w2);

ByteArrayOutputStream bout =

new ByteArrayOutputStream();

ObjectOutputStream out2 = new ObjectOutputStream(bout);

out2.writeObject("Worm storage\n");

out2.writeObject(w);

out2.flush();

ObjectInputStream in2 = new ObjectInputStream(

new ByteArrayInputStream(bout.toByteArray()));

s = (String)in2.readObject();

Worm w3 = (Worm)in2.readObject();

print(s + "w3 = " + w3);

}

}

<spoiler text="Output:">

Worm constructor: 6

Worm constructor: 5

Worm constructor: 4

Worm constructor: 3

Worm constructor: 2

Worm constructor: 1

w = :a(853):b(119):c(802):d(788):e(199):f(881)

Worm storage

w2 = :a(853):b(119):c(802):d(788):e(199):f(881)

Worm storage

w3 = :a(853):b(119):c(802):d(788):e(199):f(881)

</spoiler> Чтобы пример был интереснее, массив объектов Data в классе Worm инициализируется случайными числами. (Таким образом, нельзя заподозрить компилятор в том, что он использует дополнительную информацию для хранения объектов.) Каждый объект Wormпомечается порядковым номером-символом (char), который автоматически генерируется в процессе рекурсивного формирования связанной цепочки объектов Worm. При создании цепочки ее размер указывается в конструкторе класса Worm. Для инициализации ссылки nextрекурсивно вызывается конструктор класса Worm, однако с каждым разом размер цепочки уменьшается на единицу. В последнем сегменте цепочки ссылка next остается со значением null, что указывает на конец цепочки.

Все это делалось лишь по одной причине: для создания более или менее сложной структуры, которая не может быть сериализована тривиальным образом. Впрочем, сам акт сериализации проходит проще простого. После создания потока ObjectOutputStream (на основе другого выходного потока), метод writeObject() записывает в него объект. Заметьте, что в поток также записывается строка (String). В этот же поток можно поместить все примитивные типы, используя те же методы, что и в классе DataOutputStream (оба потока реализуют одинаковый интерфейс).

В программе есть два похожих фрагмента кода. В первом запись и чтение производится в файл, а во втором для разнообразия хранилищем служит массив байтов ByteArray. Чтение и запись объектов посредством сериализации возможна в любые потоки, в том числе и в сетевые соединения.

Из выходных данных видно, что восстановленный объект в самом деле содержит все ссылки, которые были в исходном объекте.

Заметьте, что в процессе восстановления объекта, реализующего интерфейс Serializable, никакие конструкторы (даже конструктор по умолчанию) не вызываются. Объект восстанавливается целиком и полностью из данных, считанных из входного потока InputStream.

 



<== предыдущая лекция | следующая лекция ==>
Блокировка файлов | Обнаружение класса


Карта сайта Карта сайта укр


Уроки php mysql Программирование

Онлайн система счисления Калькулятор онлайн обычный Инженерный калькулятор онлайн Замена русских букв на английские для вебмастеров Замена русских букв на английские

Аппаратное и программное обеспечение Графика и компьютерная сфера Интегрированная геоинформационная система Интернет Компьютер Комплектующие компьютера Лекции Методы и средства измерений неэлектрических величин Обслуживание компьютерных и периферийных устройств Операционные системы Параллельное программирование Проектирование электронных средств Периферийные устройства Полезные ресурсы для программистов Программы для программистов Статьи для программистов Cтруктура и организация данных


 


Не нашли то, что искали? Google вам в помощь!

 
 

© life-prog.ru При использовании материалов прямая ссылка на сайт обязательна.

Генерация страницы за: 0.43 сек.