获取父类的泛型
java反射概述
java Reflection,java中的反射(Reflection)被认为是动态语言的关键,反射机制允许程序在执行期借助Reflection API取得任何类的内部信息。并且能直接操作任意对象的内部属性和方法。
简单来说,反射就是加载类,并解剖出类的各个组成部分。
java反射机制提供的功能:
在运行时判断任意一个对象所属的类
在运行时构造任意一个类的对象
在运行时判断任意一个类所具有的成员变量和方法
在运行时调用任意一个对象的成员变量和方法 生成动态代理
与java反射相关的类:
java.lang.Class: 一个实例代表了一个运行时类
java.lang.reflect.Method:代表类的方法
java.lang.reflect.Field:代表类的成员变量
java.lang.reflect.Constructor:代表类的构造方法
我们知道java的基类是Object类,其以下方法:
public final Class getClass() 方法返回值的类型是一个Class类,值是当前对象的类(如Person.class),而此时的这个类作为Class类的一个实例。
Class类是Java反射的源头,实际上所谓反射从程序的运行结果来看也很好理解,即:可以通过对象反射求出类的名称。
正常情况下实例化对象的过程如下:
通过 类,new实例化对象。
反射机制通过 当前对象,getClass()方法返回当前对象的类。
Class类
通过反射可以得到的信息:某个类的属性、方法和构造器、某个类到底实现了哪些接口,方法的返回值类型,等等信息。
对于每个类而言,JRE 都为其保留一个不变的 Class 类型的对象。一个 Class 对象包含了特定某个类的有关信息。
1,Class本身也是一个类
2,Class 对象只能由系统建立对象(jvm)
3,一个类在 JVM 中只会有一个Class实例
4,一个Class对象对应的是一个加载到JVM中的一个.class文件
5,每个类的实例都会记得自己是由哪个 Class 实例所生成
6,通过Class可以完整地得到一个类中的完整结构
理解: * java程序经过javac.exe命令后,会生成一个或多个.class字节码文件.接着使用java.exe命令,调用JVM的类的加载器
将字节码文件加载到内存中(存放在缓存区),一个字节码文件就对应着一个运行时类。这个加载到内存中的运行时类本身就充当了Class的一个实例。
一个Class的实例就对应着一个运行时类
运行时类只会在内存中加载一次。
Class类的常用方法:
Class 没有公共构造方法。Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的。
static Class forName(String name) 返回指定类型name的Class对象,name是包含限定名的类,例如:”java.lang.Object”
Object newInstance();调用缺省无参构造器,返回Class对象的一个实例
String getName() 返回此Class对象所表示的实体(类、接口、数组类、基本类型 或void)名称,
Class getSuperClass()返回当前Class对象的父类的Class对象
Class[] getInterface()获取当前Class对象的接口
ClassLoader getClassLoader()返回该类的类加载器
Class getSuperClass()返回表示此Class实例的超类的Class 对象
Constructor[] getDeclaredConstructors()返回一个含Constructor对象的数组 Field[] getDeclaredFields()返回Field对象的一个数组
Method getMethod(String name,Class…paramType) 返回一个Method对象,此对象的形参类型为Class paramType
获取Class类对象的几种方法
1)前提:若已知具体的类,通过类的class属性获取,该方法最为安全可靠,程序性能最高
实例:Class clazz = String.class;dd//String的class属性
2)前提:已知某个类的实例,调用该实例的getClass()方法获取Class对象
实例:Class clazz =new Person().getClass();
3)前提:已知一个类的全类名,且该类在类路径下,可通过Class类的静态方法forName()获取,可能抛出ClassNotFoundException实例:Class clazz = Class.forName(“java.lang.String”);
4)其他方式//通过当前类的类加载器
ClassLoader cl = this.getClass().getClassLoader();
Class clazz4 = cl.loadClass(“全类名”);
示例分析:
通过Class类的静态方法forName(“全类名”)获取其实例
Class clazz = Class.forName(“com.atguigu.exer.Person”);
通过Class对象的getDeclaredMethod(“方法名”,Class类型的形参)方法,获取Class对象的方法。
Method eat = clazz.getDeclaredMethod(“eat”, String.class);
对象方法调用setAccessible()方法设置当前对象( )的可访问权限·· eat.setAccessible(true);
通过Class对象创建其类型的对象
Person p1 = (Person)clazz.newInstance();
对象方法调用invoke(Object 对象, 实参),并将该方法的返回结果以Object对象的形式返回;
若是静态方法或者静态属性可以使用类名.class的形式代替类的实例。
Object result = eat.invoke(p1, “apple”);
Field nation = clazz.getDeclaredField(“nation”);
nation.setAccessible(true);
nation.set(Person.class, “china”);
Object nationValue = nation.get(Person.class);
System.out.println(nationValue);
Constructor con =
clazz.getDeclaredConstructor(String.class,Integer.class,String.class);
con.setAccessible(true);
Object object = con.newInstance(“tom”,23,”China”);
System.out.println(object);
//注意的情况,int对应的class类为int.class,不能使用Integer.class.
获取父类的泛型:
Class clazz = Person.class;
Type gen = clazz.getGenericSuperclass();
ParameterizedType gensup = (ParameterizedType) gen;
Type[] type = gensup.getActualTypeArguments();
System.out.println(((Class)type[0]).getName());
类加载的过程:
当程序主动使用某一个类的时候,若该类还没有加载到内存中,则系统会通过如下方式首次加载该类,并进行初始化。
1,将类的class文件读入到内存中,并自动为之创建一个java.lang.Class类的对象。此过程由类的加载器完成。【类文件的加载】
2,将类的二进制数据合并到JRE中。【类数据的连接】
3,JVM负责将类进行初始化。
类加载器的了解:
类加载器(ClassLoader)是用来把类(class)装载进内存的。JVM 规范定义了两种类型的类加载器:启动类加载器(bootstrap)和用户自定义加载器(user-defined class loader)。
JVM在运行时会产生3个类加载器组成的初始化加载器层次结构如下:
引导类加载器(Bootstap Classloader):用C++编写的,是JVM自带的类装载器,负责Java平台核心库,用来装载核心类库。该加载器无法直接获取。
扩展类加载器(Extension ClassLoader):负责jre/lib/ext目录下的jar包或 –d java.ext.dirs 指定目录下的jar包装入工作库
系统类加载器(System ClassLoader):负责java –classpath 或 –D java.class.path所指的目录下的类与jar包装入工作 ,是最常用的加载器。
//1.获取一个系统类加载器ClassLoader classloader = ClassLoader.getSystemClassLoader();
//2.获取系统类加载器的父类加载器,即扩展类加载器classloader = classloader.getParent();
//3.获取扩展类加载器的父类加载器,即引导类加载器classloader = classloader.getParent();
获取当前类的加载器(主要通过Class对象的getClassLoader()方法获取)
ClassLoader classloader =
Class.forName(“com.atguigu.reflection.Customer”).getClassLoader();
关于类加载器的一个主要方法:
getResourceAsStream(String str):获取当前类路径下的指定文件的输入流
InputStream in = null;
in= this.getClass().getClassLoader().getResourceAsStream(“com\atguigu\reflection.properties”);
Properties pro = new Properties();
pro.load(is);
String user = pro.getProperty(“user”);
String pwd = pro.getProperty(“pwd”);
//properties文件位于当前包路径下
通过Class类对象能够做些什么?
创建类的对象:调用Class对象的newInstance()方法要求
1)类必须有一个无参数的构造器。
2)类的构造器的访问权限需要足够。
难道没有无参的构造器就不能创建对象了吗? 在操作的时候明确的调用类中的构造方法,并将参数传递进去之后,才可以实例化操作。步骤如下:
1)通过Class类的getDeclaredConstructor(Class … parameterTypes)取得本类的指定形参类型的构造器
2)向构造器的形参中传递一个对象数组进去,里面包含了构造器中所需的各个参数。
3)通过Constructor实例化对象。
示例:
//1.根据全类名获取对应的Class对象
String name = “atguigu.java.Person”;
Class clazz = null;
clazz = Class.forName(name);
//2.调用指定参数结构的构造器,生成Constructor的实例
Constructor con = clazz.getConstructor(String.class,Integer.class);
//3.通过Constructor的实例创建对应类的对象,并初始化类属性
Person p2 = (Person) con.newInstance(“Peter”,20);System.out.println(p2);
动态代理:
原理: 首先定义一个共同的接口,被代理类实现该接口, 使用一个代理类同样实现该接口,并将被代理类的对象聚合到代理类中, 然后用该代理对象取代被代理对象. 任何对(被代理)原始对象的调用都要通过代理. 代理对象决定是否以及何时将方法调用转到原始对象上。
java实现动态代理的类proxy:
Proxy :专门完成代理的操作类,是所有动态代理类的父类。
通过此类为一个或多个接口动态地生成实现类。 提供用于创建动态代理类和动态代理对象的静态方法:
Class
//1定义接口
interface ManFunction{
String info(String string);
void fly();
}
//2实现被代理类
class Man implements ManFunction{
@Override
public String info(String str) {
return str;
}
@Override
public void fly() {
System.out.println("i can fly, you can?");
}
}
//3代理类实现 被代理 的接口
class MyHandler implements InvocationHandler{
Object obj;
public MyHandler(Object obj) {
this.obj = obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
return method.invoke(obj, args);
}
}
//4创建动态代理类
class ProxyObject {
public static Object getInstanceObject(Object obj){
MyHandler in = new MyHandler(obj);
Object newProxyInstance =
Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), in);
return newProxyInstance;
}
}
在测试类中注意的问题:
1,创建被代理类对象
2,动态代理类创建动态代理,由于生成的动态代理也实现了该接口,故而可以将其转换为该接口类型,进而调用接口中的方法(也就是被代理类的方法)。
反射补充:
在调用反射到类的方法中参数是数组的情况下,例如main(String[] args),由于java1.5版本才有了可变形参,而又要兼容java1.4,故而java1.5之后反射到类的方法中有数组形参时,都会默认将数组拆开。例如:故而需要将数组行参用object类型包装。
在反射到某一个类的main(String [] args)方法,而又要调用这个方法。
Method method = clazz.getMethod(“main”,String[].class);
method.invoke(Person .class, new String[] ( “aa” ,”bb”)); 非法参数异常;
method.invoke(null, new Object[] {new String [] {” aa”, “bb”}});
或者
method.invoke(null,(Object ) new String[] {“aa”,”bb”});
反省introspector
javaBean,操作的字段都称为属性
一个get或者set方法就称为一个属性。
使用内省introspector API 操作 bean的属性。