java高级特性之反射

获取父类的泛型

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的属性。

你可能感兴趣的:(java基础)