java反射的核心理解

反射:一切的操作都将使用Object完成

如果已经有一个类,则肯定可以通过类创建对象;那么如果现在要求通过一个对象

找一个类的名称,此时就需要用到反射机制了。

public final Class getClass()返回类型是一个Class类,实际上此类是Java反射的源头。

所谓反射从程序的运行结果来看也很好理解,即可以通过对象反射求出类的名称:如下:

正常方式:引入需要的"."名称->通过new实例化->取得实例化对象

反射方式:实例化对象->getClass()方法->得到完整的.名称

 

提示:所有类的对象实际上都是Class类的实例。

javaObject类是一切类的父类,那么所有类的对象实际上也就都是java.lang.Class类的实例

所以所有的对象都可以转变为java.lang.Class类型表示

 

Class本身表示一个类的本身,通过Class可以完整地得到一个类中的完整结构,包括此类中的方法定义和属性定义等。

常用方法:

1) public static Class<?> forName(String className) throws ClassNotFoundException

传入完整的.名称实例化Class对象

2) public Constructor[] getConstructors()throws SecurityException

得到一个类中的全部构造方法

3)public Field[] getDeclaredFields()throws SecurityException

得到本类中单独定义的全部属性

4)public Field[] getFields()throws SecurityException

得到本类继承而来的全部属性

5) public Method[] getMethods()throws SecurityException

得到一个类中 的全部方法

6) public Method getMethod(String name,Class...parameter Types)

throws NoSuchMethodException,SecurityException

返回一个Method对象,并设置一个方法中的所有参数类型

7) public Class[] getInterfaces()  得到一个类中所有实现的全部接口

8) public String getName()  得到一个类完整的"."名称

9) public Package getPackage() 得到一个类的包

10) public Class getSuperclass() 得到一个类的父类

11) public Object newInstance()throws InstantiantionException,IllegalAccessException

根据Class 定义的类实例化对象

12) public Class<?>getComponentType() 返回表示数组类型的Class

13) public boolean isArray() 判断此Class是否是一个数组

Class类本身没有定义构造方法,所以如果要使用则首先必须通过forName()方法实例化对象,也可以

使用.class”对象.getClass()”方法实例化

调用有参数构造实例化对象:

只有在操作时需要明确地调用类中的构造方法,并将参数传递进去之后才可以进行实例化操作:

步骤:(1)通过Class类中的getConstructors()取得本类中的全部构造方法

(2)向构造方法中传递一个对象数组进去,里面包含了构造方法中所需的各个参数

(3)之后通过Constructor实例化对象

此处使用了Construtor类,表示构造方法。常用方法如下:

public int getMedifiers() 得到构造方法的修饰符

public String getName() 得到构造方法的名称

public Class<?>[]getParameterType()得到构造方法中参数的类型

pubic String toString()  返回此构造方法的信息

public T newInstance(Object...initrgs)throws InstantiationException,

IllegalAccessException,IllegalArgumentException,InvocationTargetException

向构造方法中传递参数,实例化对象

反射的应用--取得类的结构:在java.lang.reflect包中有以下几个类:

Constructor:表示类中的构造方法

Field:表示类中的属性

Method:表示类中的方法

这三个类都是AccessibleObject类的子类。

取得所实现的全部接口

要取得一个类所实现的全部接口,则必须使用Class类中的getInterfaces()方法.定义如下:

public Class[] getInterface()

getInterface()返回一个Class类的对象数组,之后直接利用Class类中的getName()方法输出即可

取得父类:

一具类可以实现多个接口,但是只能继承一个父类,所以如果要取得一个类的父类,可以直接使用Class类中的getSuperclass()方法。此方法定义如下:

public Class<? super T>getSuperclass()

getSuperclass()返回的是Class实例,和之前得到一个接口一样,可以通过getName()方法取得名称 

取得全部方法

使用Class类中的getMethods()方法,此方法返回一个Method类的对象数组。而如果要进一步取得方法

的具体信息,则就必须依靠Method类,此类常用方法如下:

A) public int getModifiers()  取得本方法的访问修饰符

B) public String getName() 取得方法的名称

C) public Class<?>[] getParameterTypes() 得到方法的全部参数类型

D) public Class<?> getReturnType()  得到方法的返回值类型

E) public Class<?>[] getExceptionTypes()  得到一个方法的全部抛出异常

F) public Object invoke(Object obj,Object...args) throws 

IllegalAccessException,IllegalArgumentException,InvocationTargetExceiption

通过反射调用类中的方法,此方法在后面将为读者介绍

取得全部属性:两种不同的操作

A).得到实现的接口或父类中的公共属性:public Field[] getFields()throws SecurityException

B). 得到本类中的全部属性:public Field[] getDeclaredFields()throws SecurityException

每个Field对象表示类中的一个属性,要取得属性的进一步信息,还需要Field类的常用方法如下:

1public Object get(Object obj)throws IllegalArgumentException,IllegalAccessException

得到一个对象中属性的具体内容

2public void set(Object obj,Object value)throws IllegalArgumentException,IllegalAccessException

设置指定对象中属性的具体内容

3public int getModifiers()得到属性的修饰符

4public String getName()返回属性的名称

5public boolean isAccessible()判断此属性是否可被外部访问

6public void setAccessible(boolean flag)throws SecurityException设置一个属性是否可以被 外部访问

7public statci void setAccessible(AccessibleObject[] array,boolean flag)throws SecurityExceiption

设置一组属性是否可被 外部访问

8) public String toString() 返回此Field类的信息

通过反射调用类中的方法

操作步骤如下:

1) 通过Class类的getMethod(String name,Class...parameterTypes)方法取得一个Method的对象,

并设置此方法操作时所需要的参数类型

2) 之后才可以使用invoke进行调用,并方法中传递要设置的参数

通过反射操作属性:

在反射操作中虽然可以用Method调用类中的settergetter方法设置和取得属性,但是这样操作毕竟很麻烦,所以在

反射机制中也可以直接通过Field类操作类中的属性,通过Field类提供的set()get()就可以完成 设置和取得属性内容的

操作。但是在操作前需要注意的是,在类中的所有属性已经都设置成私有的访问权限,所以在使用set()get()方法时首先要

使用Field类中的setAccessible(true)方法将需要操作的属性设置成可以被外部访问。

通过反射操作数组:

反射机制不仅只能用在类上,还可以应用在任意的引用数据类型的数据上,当然,这本身就包含了数组,即可以使用反射操作数组。

可以通过Class类的以下方法取得一个数组的Class对象

public Class<?> getComponentType()

在反射操作包java.lang.reflect中使用Array类表示一个数组,可以通过此类取得数组长度,取得数组内容的操作。

Array类常用方法如下:

public static Object get(Object array,int index)

throws IllegalArgumetnException,ArrayIndexOutOfBoundsException

根据下标取得数组内容

public static Object newInstance(Class<?> componentType,int length)throws NegativeArraySizeException

根据已有的数组类型开辟新的数组对象

public static void set(Object array,int index,Object value)throws IllegalArgumentException,ArrayIndexOutOfBoundsException

修改指定位置的内容

 

 

 

7. Java 反射 java.lang.reflect

 1) 反射是Java自我管理(对象)的机制

 2) * 可以通过反射机制发现对象的类型 发现类型的方法/属性/构造器

 3) * Java 反射 可以创建对象 并 访问任意对象方法和属性等

 4) Class 加载

 类加载到内存: java 将磁盘类文件加载到内存中,为一个对象(实例)

这个对象是Class的实例也就是 这些对象都是Class实例

 5)Class 实例代表Java中类型基本类型的类型: int.class, long.class

  类类型 Class 实例获得如下:

Class cls = String.class;

Class cls = Class.forName("java.lang.String");

Class cls = "abc".getClass();

  以上方法获得cls 是同一个对象就是String 类内存加载的结果’  动态代理

之前曾为读者讲解过代理机制的操作,但是所讲解的代理设计属于静态代理,

因为每一个代理类只能为一个接口服务,这样一来程序开发中必然会产生过多的代理。

最好的做法是可以通过一个代理类完成全部的代理功能,那么此时必须使用动态代理完成。

java中要想实现动态代理机制,则需要java.lang.reflect.InvocationHandler接口和

java.lang.reflect.Proxy类的支持。

InvocationHandler接口的定义如下:

public Object invoke(Object proxy,Method method,Object[] args) throws Throwable{}

在此接口中只定义了一个invoke()方法,此方法中有3个参数,意义如下:

Object proxy:被代理的对象

Method method:要调用的方法

Object args[]:方法调用时所需要的参数

Proxy类是专门完成代理的操作类,可以通过此类为一个或多个接口动态地生成实现类。Proxy类提供了如下的操作方法:

public static Object newProxyInstance(ClassLoader loader,

Class<?>[] interfaces,InvocationHandler h) throws IllegaArgumentException

通过newProxyInstance()方法可以动态地生成实现类,此方法中的参数意义如下:

ClassLoader loader:类加载器

Class<?>[] interfaces:得到全部的接口

InvocationHandler h :得到InvocationHandler接口的子类实例

 

提示:类加载器

在Proxy类的newProxyInstance()方法中需要一个ClassLoader类的实例,ClassLoader实际上对应的是类加载器,

java中主要有以下3种类加载器。

BootStrap ClassLoader:此加载器采用C++编写,一般开发中是看不到的。

Extension ClassLoader:用来进行扩展类的加载,一般对应的是jre\lib\ext目录中的类。

AppClassLoader:加载classpath指定的类,是最常使用的一种加载器。

装饰模式

流的构造就是一个装饰模式的完整应用。节点流就像是一个最原始的元素,拥有有限的能力,过滤流就像是各种装饰

元素,它们通过节点流来构造一个功能强大的流,如 BufferedInputStream,DataInputStream. . .

 

字节流与字符流之间的‘ 桥接器‘ ,对于输出流来说,它可以把字节流转变成字符流,(InputStreamReader

 而对于输入流来说,它可以把字符流转换成字节流,,(OutputStreamWriter;

 所以,用一个字节输入流来构造一个字符流如下:

BufferedReader br = new BufferedReader(new InputStreamReader(new InputStream());

如果想从键盘获得收入:可以这样封装一个 BufferedReader;

 BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

当然,对于桥梁,是可以指定字符编码方式的

注:我们在多线程讲过,等待数据输入是会产生阻塞的。所以read()方法是会产生阻塞的。

而对于 BufferdReader 中的 readLine() 方法,只有读到换行,它才会打破阻塞!一定要注意!

 

 

现在,我们来介绍如何让一个对象持入化呢?

实现一个接口: Serializable 接口

这是一个标记接口,不需要实现任何方法

实现了 Serializable 接口的类的对象就可以序列化,注:如果此类中包含其它的类对象,则那个类也必需要实现

Serializable 接口。

其实,序列化一个对象,就是序列化它所包含的属性。

那么如何有先择地序列化其中某些属性,而某些属性是不序列化呢?

关键字: transient 它用来修饰实例变量表示此实例变量不被序列化。

如: class Student implements Seriailizable {

          String name;

          int transient age; //在写对象时,age 属性将不被序列化,在读到此属性时,它的值为 0

     }

注:Serializable 接口不能自己定义序列化逻辑,它的子接口:Externalizable 接口,它可以让你自己定义序列化

对象的逻辑。 详见 API, 这里不多做介绍

小结:

1. 使用 ObjectInputStream 和 ObjectOutputStream 来读写对象

2. 对象必须要实现 Serializable 接口

3. 属性是对象,也要实现 Serializable 接口

4. 关键字 transient 来让属性不序列化

5. 用 Externalizable 来定义自己的序列化逻辑。

类: RandomAccessFile 它实现了 DataInput, DataOutput 接口; 可读/可写

       此类可以访问一个文件中的随机位置(并不一定是从头到尾)

方法: 

getFilePoint() //获得此文件中的当前偏移量

         seek(long pos); //定位当前指针的偏移量

         read(), read(byte[] buf), read(byte[] buf, int off, int len);

         write(int b), write(byte[] buf), write(byte[] buf, int off, int len);

 

 

() ObjectInputStream 

对以前使用 ObjectOutputStream 写入的基本数据和对象进行反序列化。

ObjectOutputStream 和 ObjectInputStream 分别与 FileOutputStream 和 FileInputStream 一起使用时,

可以为应用程序提供对对象图形的持久存储。

只有支持 java.io.Serializable 或 java.io.Externalizable 接口的对象才能从流读取。 

readObject 方法用于从流读取对象。应该使用 Java 的安全强制转换来获取所需的类型。

在 Java 中,字符串和数组都是对象,所以在序列化期间将其视为对象。读取时,需要将其强制转换为期望的类型。

可以使用 DataInput 上的适当方法从流读取基本数据类型。 

 

例如,要从由 ObjectOutputStream 中的示例写入的流读取:

 

        FileInputStream fis = new FileInputStream("t.tmp");

        ObjectInputStream ois = new ObjectInputStream(fis);

 

        int i = ois.readInt();

        String today = (String) ois.readObject();

        Date date = (Date) ois.readObject();

 

        ois.close();

() ObjectOutputStream extends OutputStream

ObjectOutputStream 将 Java 对象的基本数据类型和图形写入 OutputStream

writeObject 方法用于将对象写入流中。所有对象(包括 String 和数组)都可以通过 writeObject 写入。

可将多个对象或基元写入流中。必须使用与写入对象时相同的类型和顺序从相应 ObjectInputstream 中读回对象。

writeObject 方法负责写入特定类的对象状态,以便相应的 readObject 方法可以恢复它

 

例如,要写入可通过 ObjectInputStream 中的示例读取的对象,请执行以下操作:

 

        FileOutputStream fos = new FileOutputStream("t.tmp");

        ObjectOutputStream oos = new ObjectOutputStream(fos);

 

        oos.writeInt(12345);

        oos.writeObject("Today");

        oos.writeObject(new Date());

 

        oos.close();

  

 () ByteArrayInputStream extends InputStream

  ByteArrayInputStream 包含一个内部缓冲区,该缓冲区包含从流中读取的字节。内部计数器跟踪 read 方法要提供的下一个字节。

关闭 ByteArrayInputStream 无效。此类中的方法在关闭此流后仍可被调用,而不会产生任何 IOException。 

 

ByteArrayOutputStream extends OutputStream

此类实现了一个输出流,其中的数据被写入一个 byte 数组。

缓冲区会随着数据的不断写入而自动增长。可使用 toByteArray() 和 toString() 获取数据。 

toByteArray()  创建一个新分配的 byte 数组。返回:byte[]

转载于:https://www.cnblogs.com/jiadongpo/archive/2013/04/10/3012717.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值