programowane refleksyjne i serializacjausers.uj.edu.pl/~ciesla/java/java_12.pdf · języka...

28
Programowane refleksyjne i serializacja 1 1. Programowanie refleksyjne: przegląd wybranych klas z pakietu java.lang i java.lang.reflect, dynamiczne obiekty proxy. 2. Serializacja interfejs Serializable, pełna kontrola nad serializacją, serializacja do plików XML.

Upload: lynhan

Post on 01-Mar-2019

223 views

Category:

Documents


0 download

TRANSCRIPT

Programowane refleksyjne i serializacja

1

1. Programowanie refleksyjne:

● przegląd wybranych klas z pakietu java.lang i java.lang.reflect,

● dynamiczne obiekty proxy.

2. Serializacja

● interfejs Serializable,

● pełna kontrola nad serializacją,

● serializacja do plików XML.

Wprowadzenie

2

Programowanie refleksyjne polega na dynamicznym korzystaniu ze struktur, które

języka programowania, które nie musiały być zdeterminowane w momencie

tworzenia oprogramowania.

Najważniejsze klasy języka Java, które umożliwiają programowanie refleksyjne to

Class, Field, Method, Array, Constructor. Są one zgrupowane w pakietach

java.lang i java.lang.reflect.

Klasa Class

3

Class reprezentuje klasę. Każdy obiekt (java.lang.Obiect) w Javie zawiera

metodę getClass(), która zwraca instancję klasy Class.

Podstawowe metody Class:

static Class forName(String) – zwraca obiekt reprezentujący klasę

o podanej nazwie. Jeśli klasa nie była wcześniej używana jest wcześniej ładowana za

pomocą odpowiedniego ClassLoader'a.

Class.forName("com.mysql.jdbc.Driver");

Object newInstance()– tworzy nowy obiekt (instancję) danego typu

Object obj = new Object() jest równoważne

Object obj = Class.forName("java.lang.Object").newInstance()

Klasa ClassLoader

4

Metoda getClassLoader() wywołana na rzecz obiektu Class zwraca obiekt

odpowiedzialny za załadowanie zasobów związanych z daną klasą. Obiekty ClassLoader mogą być wykorzystane do dynamicznej zmiany miejsca z którego

zostaną załadowane zasoby.

class OwnClassLoader extends ClassLoader { public Class findClass(String name) { byte[] b = loadClassData(name); return defineClass(name, b, 0, b.length); }

private byte[] loadClassData(String name) { // załaduj klasę z ustalonej lokalizacji . . . } }

Klasa ClassLoader

5

Zastosowanie własnego ClassLoader'a:

...OwnClassLoader cl = new OwnClassLoader();Class c = cl.loadClass("my.own.MyClass");Object obj = c.newInstance();MyClass myObj = (MyClass)obj;

...

Istnieją standardowe rozszerzenia podstawowego ClassLoader'a. Są to:

SecureClassLoader i URLClassLoader:

...URL[] urlArray = {new URL("http://www.moje.klasy.pl"),

new URL("http://klasy.kolegi.pl") };URLClassLoader loader = URLClassLoader.newInstance(urlArray);Class c = loader.loadClass("my.own.MyClass");

...

Klasa Class

6

boolean isAssignableFrom(Class) – czy to ta sama klasa, nadklasa czy

nadinterfejs dla argumentu.

boolean isInstance(Object obj) – czy obiekt jest instancją klasy lub

podklasy.class C1{}class C2 extends C1{}...

C1 o1 = new C1();C2 o2 = new C2();

o1.getClass().isAssignableFrom(o2.getClass()); // trueo2.getClass().isAssignableFrom(o1.getClass()); // falseo1.getClass().isInstance(o2); // trueo2.getClass().isInstance(o1); // false

...

Klasa Class

7

Inne metody obiektu Class:

boolean isArray() - czy to tablica.

boolean isInterface() - czy interfejs.

boolean isPrimitive() - czy typ prymitywny (int, float, itd.).

Constructor[] getConstructors() - zwraca tablicę publicznych

konstruktorów.

Constructor[] getDeclaredConstructors() - zwraca tablicę wszystkich

konstruktorów.

Constructor getConstructor(Class[]) - zwraca publiczny konstruktorów

o zadanych typach argumentów.

Klasa Class

8

Field[] getFields() - zwraca tablicę publicznych atrybutów,

Field getField(String) - zwraca publiczny atrybut o podanej nazwie,

Class[] getInterfaces() - zwraca interfejsy implementowane przez klasę.

Method getMethod(String, Class[]) - zwraca publiczną metodę o podanej

nazwie i sygnaturze.

Method[] getMethods() - zwraca wszystkie publiczne metody należące do klasy

(wraz z metodami implementującymi interfejsy oraz metodami dziedziczonymi

z nadklas i nadinterfejsów)

Klasa Field

9

Wybrane metody:

String getName() - zwraca nazwę atrybutu.

Class getType() - zwraca typ atrybutu.

int getModifiers() - zwraca rodzaj atrybutu (public, private, ...).

Object get(Object) - zwraca wartość atrybutu we wskazanym obiekcie.

void set(Object obj, Object value) – ustawia wartość atrybutu we

wskazanym obiekcie.

Klasa Field – przykład 1

10

import java.lang.reflect.Field;

public class MyClass {public int liczba_naturalna;protected double liczba_double;private String tekst;float liczba_rzeczywista;public static void main(String[] args) {

MyClass obj = new MyClass();Field[] fa = obj.getClass().getDeclaredFields();for (int i = 0; i < fa.length; i++) {

System.out.print(fa[i].getModifiers());System.out.print(" " + fa[i].getType().getName());System.out.println(" " + fa[i].getName());

}}

}

Klasa Field – przykład 1

11

Wynik działania:1 int liczba_naturalna4 double liczba_double2 java.lang.String tekst0 float liczba_rzeczywista

Liczby zwracane przez getModifier() mogą być zamienione na odpowiadające

im wartości tekstowe za pomocą Modifier.toString(int). Wszystkie możliwe

modyfikatory to: abstract, final, interface, native, private, protected, public, static, strictfp, synchronized, transient, volatile.

Klasa Field – przykład 2

12

...MyClass obj = new MyClass();Field[] fa = obj.getClass().getDeclaredFields();System.out.println(obj.liczba_naturalna);try {

fa[0].set(obj, new Integer(15));} catch (IllegalArgumentException e) {

e.printStackTrace();} catch (IllegalAccessException e) {

e.printStackTrace();}System.out.println(obj.liczba_naturalna);

...Wynik działania:015

Klasa Method

13

Wybrane metody:

Object invoke(Object obj, Object[] args) – wywołuje metodę na rzecz

obiektu obj. Argumenty wejściowe są przekazywane przez tablicę args. Wartością

zwracaną jest wartość zwrócona w wyniku działania metody.

Przykład:

...Method m = Class.forName("MyClass").getDeclaredMethod(

"example3", null);m.invoke(null, null);

...

metoda invoke może zwrócić kilka rodzajów wyjątków związanych z dostępem do

metody i zgodnością typów argumentów.

Klasa Method

14

Wady wynikające z urzywania invoke(Object obj, Object[] args):

● typy primitywne (int, float, ...) muszą zostać opakowane w odpowiednie klasy

(Integer, Float, ...) tak, aby można było je przekazać przez tablicę parametrów,

● brak kontroli (w trakcie kompilacji) typów przekazywanych parametrów,

● ograniczenie obsługi wyjątków do Throwable.

Rozwiązaniem tych problemów jest możliwe dzięki tzw. Dynamic Proxy Class.

Klasa Proxy

15

...m.invoke(obj);...

Program...public void metoda(){...}...

Obiekt (klasa)

...proxy.metoda();...

Program...public void metoda(){...}...

Obiekt (klasa)

...obj.metoda();...

Proxy

brak kontroli typów

proxy musi być tworzone dynamicznie!

Klasa Proxy

16

Klasa Proxy udostępnia metody statyczne służące do tworzenia tzw. dynamicznych

klas proxy oraz ich instancji:

Utworzenie proxy dla określonego interfejsu (np. MyInterface):

InvocationHandler handler = new MyInvocationHandler(...);Class proxyClass = Proxy.getProxyClass(

MyInterface.class.getClassLoader(), new Class[] { MyInterface.class });

MyInterface mi = (MyInterface) proxyClass.getConstructor(

new Class[] { InvocationHandler.class }).newInstance(new Object[] { handler });

Klasa Proxy

17

Alternatywne wywołanie:

MyInterface mi = (MyInterface) Proxy.newProxyInstance(MyInterface.class.getClassLoader(), // loader dla zasobównew Class[] { MyInterface.class }, // tablica interfejsówhandler); // obiekt do którego będą przekazywane

// wywołania

Stworzono obiekt mi, który „z zewnątrz” wygląda jak klasa implementująca

MyInterface, natomiast obsługa metod będzie w rzeczywistości realizowana przez

handler.

Klasa Proxy

18

Obiekt handler nie musi implementować MyInterface! Musi za to

implementować interfejs InvocationHandler, czyli metodę:

Object invoke(Object proxy, Method method, Object[] args)

Wszystkie wywołania metod na obiekcie mi zostaną przekierowane do metody

invoke, przy czym pierwszym parametrem będzie obiekt proxy.

Klasa Proxy

19

...Pisarz obj = new PisarzImpl();Method m = obj.getClass().getMethod( "pisz", new Class[] {String.class});m.invoke(obj, new Object[]{"hello world"});...

class PisarzImpl implements Pisarz {...public void pisz(String s){

...}...

}interface Pisarz {

public void pisz(String s);}

Klasa Proxy

20

class MyHandler{ private Pisarz obj = new PisarzImpl(); public static Object invoke(Object proxy, Method m, Object[] args)){ return m.invoke(obj, args); }}

...Pisarz p = (Pisarz) Proxy.newProxyInstance(

Pisarz.class.getClassLoader(),new Class[] { Pisarz.class },

new MyHandler());p.pisz("hello world");...

class PisarzImpl implements Pisarz {...public void pisz(String s){

...}...

}interface Pisarz {

public void pisz(String s);}

Serializacja

21

Serializacja polega na zapisaniu stanu aktywnego obiektu. Tak zapisany obiekt może

być potem przeniesiony (przesłany) w inne miejsce i tam odtworzony.

Aby obiekt mógł być serializowany musi implementować interfejs

java.io.Serializable. Nie posiada on żadnych metod a jedynie informuje JVM, że

obiekt może być zapisywany.

Aby umożliwić serializacje podtypów nieserializowalnych klas należy przejąć

odpowiedzialność za serializację wszystkich dostępnych pól. Podtyp może przejąć

odpowiedzialność tylko wtedy, gdy rozszerzana klasa posiada dostępny,

bezargumentowy konstruktor. W przeciwnym razie serializacja zakończy się błędem.

Serializacja

22

Klasy wymagające specjalnego traktowania w trakcie serializacji powinny

implementować następujące metody:private void writeObject(java.io.ObjectOutputStream out) throws IOExceptionprivate void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException;

Jeśli serializacja ma polegać na zapisie innego obiektu to klasy powinna

implementować:dowolny_modyfikator Object writeReplace() throws ObjectStreamException;dowolny_modyfikator Object readResolve() throws ObjectStreamException;

Serializacja - ObjectOutputStream

23

Aby zapisać lub odczytać klasę należy utworzyć obiekt ObjectOutputStream

a następnie skorzystać z metod:

void writeObject(Object)Object readObject()

w celu zapisania lub odczytania obiektu.

Serializacja - przykład

24

Zapisywanie:

...FileOutputStream out = new FileOutputStream("magazyn");ObjectOutputStream s = new ObjectOutputStream(out);s.writeObject("Hello World");s.writeObject(new Date());s.flush();...Odczytywanie:...FileInputStream in = new FileInputStream("magazyn");ObjectInputStream s = new ObjectInputStream(in);String today = (String)s.readObject();Date date = (Date)s.readObject();...

Pełna kontrola nad serializacją

25

● implementacja writeObject(ObjectOutputStream) oraz readObject():private void writeObject(ObjectOutputStream os) throws IOException{

os.defaultWriteObject();// własny kod

}private void readObject(ObjectInputStream s) throws IOException { s.defaultReadObject(); // własny kod, aktualizacja stanu obiektu}

● implementacja interfejsu Externalizable:

public interface Externalizable extends Serializable {

public void writeExternal(ObjectOutput out) throws IOException;

public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException;

Serializacja a XML

26

Wraz z wersją 1.4 Javy udostępniona została możliwość serializacji obiektów typu

JavaBeans do plików tekstowych w formacie XML. Służą do tego klasy z pakietu

java.beans:

XMLEncoder:

XMLEncoder e = new XMLEncoder(new FileOutputStream("jbutton.xml")));e.writeObject(new JButton("Hello world"));e.close();

XMLDecoder:

XMLDecoder d = new XMLDecoder(new FileInputStream("jbutton.xml"));

obj = d.readObject();d.close();

Serializacja a XML

27

Zawartość pliku XML:

<?xml version="1.0" encoding="UTF-8"?>

<java version="1.5.0_04" class="java.beans.XMLDecoder">

<object class="javax.swing.JButton"> <string>Hello world</string> </object> </java>

Ciekawostka:

Objętość pliku XML: 189 bajtów, objętość pliku binarnego 4329 bajtów!

Podsumowanie

28

Programowanie refleksyjne i serializacja wydatnie zwiększają przydatność języka

programowania do tworzenia zaawansowanych, wysoce konfigurowalnych

projektów. Serializacja ponadto leży u podstaw wielu technik związanych z szeroko

rozumianą wymianą danych a wśród nich przede wszystkim programowania

rozproszonego.