您的位置:首页 > 编程语言 > Java开发

java加强--反射(补充泛型)

2013-09-26 13:23 351 查看
        能够分析类能力的程序被称为反射 reflective

  通过一个具体的类型可以有很多该类型的实例,比如学生,学生实例有学生A,学生B....,比如老师有C老师,D老师等。这些不同职业的人又属于一个共同的类型:人。在java中也有类似的关系,Person类可以有很多个Person实例p,Food类又可以有很多Food类的实例对象f,这些不同描述的类型又属于一个共同的类型Class。

        通过Class类可以得到指定类的相关信息,获取一个类对应的Class对象的方法有三种:

        1、getClass方法

                java.lang.Object中定义的getClass方法返回与调用该方法对象所属类关联的Class对象,所有的java对象都有这个方法。

        2、.class

                类名.class会返回与该类对应的Class对象。

        3、Class.forName("String fullClassName")该方法可以根据字符串参数所指定的类名获取与该类关联的Class对象。如果该类还没有被装入,该方法会将该类装入JVM。

        当类已经被加载到内存中了,可以通过第1和第2个方法获取Class对象,当类还没被加载到内存中时则需要用类加载器或者一个对象来调用方法3来获取Class对象。

获取类的构造函数并创建该字节码对象:

Constructor con = String.class.getConstructor(StringBuffer.class);
String str = (String)constructor.newInstance(new StringBuffer("abc"));


获取类的成员变量:

假设有类Reflect,有成员变量int x,int y。构造函数public Reflect(int x,int y){this.x=x;this.y=y;}

Reflect re = new Reflect(1,2);
Field fieldX = re.getClass().getField("y");//指定变量名,得到Field类型的对象。
fieldX.get(re);//得到指定对象的x的值


下面是我自己的理解,可能也有些不太合适的地方:好比人,对于每个实例对象如张三李四,都有属于自己的器官如肝肾脾,于是就有 Organ organ = 张三.getClass().getOrgan("肝")。由于前面的张三.getClass()得到的是人类型的,所以后面的getOrgan("肝")并不是指定对象的肝,而是人类型的肝类型,要想得到指定对象的肝可以使用organ.get("张三"),这样就得到了张三的肝。

访问私有成员变量用getDeclaredField(String name),同样的,每个人的肝都是私有的,通过该方法并不能对别人的肝进行操作,因为没有操作权限。但是可以通过暴力反射使之能够被访问,organ.setAccessible(true)。

 

JDK1.5中关于Class的描述:

"Class
类的实例表示正在运行的 Java 应用程序中的类和接口。枚举是一种类,注释是一种接口。每个数组属于被映射为 Class 对象的一个类,所有具有相同元素类型和维数的数组都共享该
Class
对象。基本的 Java 类型(
boolean
byte
char
short
int
long
float

double
)和关键字
void
也表示为
Class
对象。

Class
没有公共构造方法。
Class
对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的
defineClass
方法自动构造的."

我的理解是,java中的各种类和接口(Runnable,String)都是类型为Class的实例对象,相同元素类型和维数的数组都映射到同一个Class实例对象,比如一维int数组共享一个类名为“[I”的Class实例对象,二维String数组共享一个类名为“[[Ljava.lang.String”的Class实例对象 。

另外在用反射调用一个字节码的main方法时,需要传入一个String数组,此时传入一个数组对象。但是虚拟机会对传入的数组对象进行自动拆封,真正被传进去的只有数组角标为0的元素。此时可以将数组对象再封装成一个新数组的0号角标元素,或者将数组对象强制转换成Object类型,告诉虚拟机不能拆封。

===================补充=================

反射在实际应用开发中一般被用来做框架,一个框架就像是一个毛坯房,用户根据自己的需求对毛坯房进行装修达到他们想要的效果;同理,程序员可以在框架的基础上进行代码的编写达到想要的功能。

在写框架的时候无法知道要被调用的类名,所以无法直接使用new来创建某个类的实例对象,需要用发射来实现。

另外使用Properties在从某个配置文件中读取配置信息时需要使用到的一个模版代码:

//创建一个文件读取流

InputStream in = new FileInputStream(filename);

//创建Properties对象

Properties props = new Properties();

//加载文件流中的配置信息

props.load(in);

//加载完成后,可以马上关闭流文件。打开文件流使用操作系统底层的方法,关闭之后in对象将由GC进行回收。

in.close();

===================2013.11.9补充<泛型>=================

<E>  ----- E称为类型变量或参数类型

<>    ----- 读作 typeof

泛型技术主要应用在编译时期。在编译时期,编译器负责参数类型的检测,到了运行时期,即生成了字节码后,所包含的泛型定义的类型信息就不存在了(或说被擦除了),这就是泛型的类型擦除,在生成的字节码中无类型信息,这也是虚拟机的局限性导致的。

import java.util.ArrayList;
public class GenericDemo
{
public static void main(String[] args)  {
//泛型类型为String
ArrayList<String> at1 = newArrayList<String>();
//泛型类型为Integer
ArrayList<Integer>at2 = newArrayList<Integer>();

//运行结果为true,说明都是同一份字节码,已擦除了泛型类型了
System.out.println(at1.getClass() ==at2.getClass());
}
}


泛型是针对编译器的,通过反射可以绕过编译器对泛型类型的检查:

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

public class ReflectDemo{
public static void main(String ... args) throws Exception{
test_1();
//test_2();
}
private static void test_2(){
//未使用泛型限制,虽然元素都能存储和取出,但是安全性却大大降低了!
List list = new ArrayList();
list.add("string ...");
list.add(123);
list.add(true);
Object o1 = list.get(0);
Object o2 = list.get(1);
Object o3 = list.get(2);
System.out.println(o1);
System.out.println(o2);
System.out.println(o3);
}
private static void test_1() throws NoSuchMethodException,
IllegalAccessException, InvocationTargetException {
//使用泛型限制了元素类型
List<String> list = new ArrayList<String>();
Class clazz = list.getClass();
System.out.println(clazz.getName());
//使用反射绕开编译器
Method method = clazz.getMethod("add", Object.class);
method.invoke(list, new Integer(20));
//这里的get函数,编译器认为取出的是String类型
Object o = list.get(0);
System.out.println(o);
}
}


通配符<?>和写具体泛型参数<E>有的不同:

通配符?不能进行具体对象的操作,即不能调用与参数化有关的方法,因为?是不确定的类型,不具备对象,只能用些跟对象无关的方法,如集合的ArrayList原始类型的size()方法,与集合元素类型无关。

而泛型参数E是有对象的,可进行具体的对象的操作,是种限定类型, 如迭代器输出时,可以E e = it.next();而后对e进行对象操作。也可进行(E)”abc”;之类的强制转换。

另外,在泛型结构的内部也要使用泛型参数时,就需定义泛型参数E了。

参数化类型不考虑继承关系:

Vector<String> v = new Vector<Object>();   
×


Vector<Object> v = new Vector<String>();     ×

Vector v = new Vector<String>();

Vector<Object> v1 = v;        //这两句能顺利编译通过

通过反射获取泛型的参数化类型:

importjava.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
importjava.lang.reflect.Type;
importjava.util.Date;
importjava.util.Vector;

public class GenericReflect
{
public static voidmain(String[] args) throws Exception
{
//获取fun方法
Method method =GenericReflect.class.getMethod("fun", Vector.class);
//Type类型是Calss的父类,Method类中提供了获取泛型参数的方法
Type[] types =method.getGenericParameterTypes();
// ParameterizedType是Type的子类
ParameterizedTypepType = (ParameterizedType)types[0];
//获取泛型的原始类型
System.out.println(pType.getRawType());
//获取泛型的参数变量
System.out.println(pType.getActualTypeArguments()[0]);
}
public static voidfun(Vector<Date> vd)
{
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  java 反射 泛型
相关文章推荐