知识屋:更实用的电脑技术知识网站
所在位置:首页 > 教育

【Java】反射

发表时间:2022-03-25来源:网络

一、反射的概述

1. 反射的简介

Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。

加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,我们形象的称之为:反射。

通常的方式:引入需要的“包类”名称---->通过“new”实例化---->获得实例化对象

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

框架 = 注解 + 反射 + 设计模式

2. 反射动态性体现

只有当程序运行时我们才能知道调用的类

@Test public void test2(){ for(int i = 0;i < 100;i++){ int num = new Random().nextInt(3);//0,1,2 String classPath = ""; switch(num){ case 0: classPath = "java.util.Date"; break; case 1: classPath = "java.lang.Object"; break; case 2: classPath = "com.atguigu.java.Person"; break; } try { Object obj = getInstance(classPath); System.out.println(obj); } catch (Exception e) { e.printStackTrace(); } } } /* 创建一个指定类的对象。 classPath:指定类的全类名 */ public Object getInstance(String classPath) throws Exception { Class clazz = Class.forName(classPath); return clazz.newInstance(); }

3. 反射机制提供的功能

在运行时判断任意一个对象所属的类

在运行时构造任意一个类的对象

在运行时判断任意一个类所具有的成员变量和方法

在运行时获取泛型信息

在运行时调用任意一个对象的成员变量和方法

在运行时处理注解

生成动态代理

代码示例

@Test public void test1() throws Exception { Class clazz = Person.class; //1.通过反射,创建Person类对象 Constructor cons = clazz.getConstructor(String.class, int.class); Person person = cons.newInstance("Tom", 12); System.out.println(person);//Person{name='Tom', age=12} //2.通过反射,调用对象指定的属性、方法 //调用属性 Field age = clazz.getDeclaredField("age"); age.setAccessible(true); age.set(person, 10); System.out.println(person.toString());//Person{name='Tom', age=10} //调用方法 Method show = clazz.getDeclaredMethod("show"); show.invoke(person);//my name is Tom and age is 10 System.out.println("==================================="); //通过反射,可以调用Person类的私有结构的。比如:私有的构造器、方法、属性 //调用私有的构造器 Constructor cons1 = clazz.getDeclaredConstructor(String.class); cons1.setAccessible(true); Person p1 = cons1.newInstance("Bruce"); System.out.println(p1);//Person{name='Bruce', age=0} //调用私有的属性 Field name = clazz.getDeclaredField("name"); name.setAccessible(true); name.set(p1, "Jarry"); System.out.println(p1); //调用私有的方法 Method nation = clazz.getDeclaredMethod("nation", String.class); nation.setAccessible(true); Object nation1 = (String) nation.invoke(p1, "China");//相当于String nation = p1.showNation("China") System.out.println(nation1);//I come from China }

4. 相关API

java.lang.Class:反射的源头 java.lang.reflect.Method java.lang.reflect.Field java.lang.reflect.Constructor ......

二、Class类

1. Class简述

在Object类中定义了以下的方法,此方法将被所有子类继承:
public final Class getClass()

以上的方法返回值的类型是一个Class类,此类是Java反射的源头,实际上所谓反射从程序的运行结果来看也很好理解,即可以通过对象反射求出类的名称。

对象使用反射后可以得到的信息:某个类的属性、方法和构造器、某个类到底实现了哪些接口。对于每个类而言,JRE都为其保留一个不变的Class类型的对象。一个Class对象包含了特定某个结构( class/interface/enum/annotation/primitive type/void/[])的有关信息。 Class本身也是一个类 Class对象只能由系统建立对象 一个加载的类在JVM中只会有一个Class实例 一个Class对象对应的是一个加载到JVM中的一个.class文件 每个类的实例都会记得自己是由哪个Class实例所生成 通过Class可以完整地得到一个类中的所有被加载的结构 Class类是Reflection的根源,针对任何你想动态加载、运行的类,唯有先获得相应的Class对象

类的加载过程:

程序经过javac.exe命令以后,会生成一个或多个字节码文件(.class结尾)。接着我们使用java.exe命令对某个字节码文件进行解释运行。相当于将某个字节码文件加载到内存中。此过程就称为类的加载。加载到内存中的类,我们就称为运行时类,此运行时类,就作为Class的一个实例。 换句话说,Class的实例就对应着一个运行时类。 加载到内存中的运行时类,会缓存一定的时间。在此时间之内,我们可以通过不同的方式来获取此运行时类。

2. Class类的常用方法

代码示例:

//创建Class的实例 String str = "test1.Person"; Class clazz = Class.forName(str); //调用Class的空参构造器创建对象 Object obj = clazz.newInstance; //获取clazz的name属性 Field field = clazz.getField("name"); field.set(obj,"Jarry"); Object name = filed.get(obj); System.out.println(name); //test1.Person为test1包下的Person类

3. 获取Class实例的几种方式:

已知具体的类,通过类的class属性获取,该方法最为安全可靠,程序性能最高

实例:Class clazz = String.class;

已知某个类的实例,调用该实例的getClass()方法获取Class对象

实例:Class clazz = person.getclass();

已知一个类的全类名,且该类在类路径下,可通过Class类的静态方法forName()获取, 可能抛出 ClassNotFoundException(比较常用)

实例:Class clazz = Class.forName(String classPath)

通过类加载器

实例:ClassLoader cl = this.getclass().getClassLoader(); Class clazz = cl.loadClass("类的全类名");

代码示例

@Test public void test2() throws ClassNotFoundException { //方式一:调用运行时类的属性:.class Class clazz1 = Person.class; System.out.println(clazz1);//class cn.bruce.java.Person //方式二:通过运行时类的对象,调用getClass() Person p1 = new Person(); Class clazz3 = Class.forName("cn.bruce.java.Person"); System.out.println(clazz3);//class cn.bruce.java.Person System.out.println(clazz1 == clazz2);//true System.out.println(clazz1 == clazz3);//true //方式四:使用类的加载器:ClassLoader (了解) ClassLoader classLoader = ReflectionTest.class.getClassLoader(); Class clazz4 = classLoader.loadClass("cn.bruce.java.Person"); System.out.println(clazz4);//class cn.bruce.java.Person System.out.println(clazz1 == clazz4);//true }

总结:创建类的对象的方式?

方式一:new + 构造器

方式二:要创建Xxx类的对象,可以考虑:Xxx、Xxxs、XxxFactory、XxxBuilder类中查看是否有静态方法的存在。可以调用其静态方法,创建Xxx对象。

方式三:通过反射

4. Class实例可以代表的结构

class:外部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类

interface:接口

[]:数组

enum:枚举

annotation:注解@interface

primitive type:基本数据类型

void

在Java中万事万物皆对象

代码示例

@Test public void test3(){ Class c1 = Object.class; Class c2 = Comparable.class; Class c3 = String[].class; Class c4 = int[][].class; Class c5 = ElementType.class; Class c6 = Override.class; Class c7 = int.class; Class c8 = void.class; Class c9 = Class.class; int[] i1 = new int[10]; int[] i2 = new int[100]; Class[] getInterfaces() 确定此对象所表示的类或接口实现的接口。

2.所继承的父类: public Class getParameterTypes();

4.全部的方法:
public Method[] getDeclaredMethods():返回此Class对象所表示的类或接口的全部方法
public Method[] getMethods():返回此Class对象所表示的类或接口的public的方法
Method类中:

public Class getReturnType()取得全部的返回值 public Class[] getParameterTypes()取得全部的参数 public int getModifiers()取得修饰符 public Class [] getEXceptionTypes()取得异常信息

5.全部的Field:
public Field[] getFields():返回此Class对象所表示的类或接口的public的Field。
public Field[] getDeclaredFields():返回此Class对象所表示的类或接口的全部Field
Field方法中:

public int getModifiers()以整数形式返回此Field的修饰符 public Class getType()得到Field的属性类型 public String getName()返回Field的名称。

6.Annotation相关
get Annotation(Class annotationClass)
getDeclaredAnnotations()

7.泛型相关
获取父类泛型类型:Type getGenericSuperclass()
泛型类型:ParameterizedType
获取实际的泛型类型参数数组:getActualTypeArguments()

8.类所在的包 Package getPackage()

getMethod():返回某个类的所有公用(public)方法包括其继承类的公用方法,当然也包括它所实现接口的方法 getDeclaredMethod():对象表示的类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。当然也包括它所实现接口的方法 setAccessible(true):保证当前方法是可访问的,对于私有方法,需要设置当前方法可访问,否则默认不可访问

2.2 代码示例

获取属性Fieled

public class FiledTest { @Test public void test1() { Class clazz = Person.class; //获取属性结构 //getFields():获取当前运行时类及其父类中声明为public访问权限的属性 Field[] fields = clazz.getFields(); for (Field f : fields) { System.out.println(f); } System.out.println(); //getDeclaredFields():获取当前运行时类中声明的所有属性。(不包含父类中声明的属性) Field[] declaredFields = clazz.getDeclaredFields(); for (Field f : declaredFields) { System.out.println(f); } } //权限修饰符 数据类型 变量名 @Test public void test2() throws ClassNotFoundException { Class clazz = Class.forName("cn.bruce.java1.Person"); Field[] declaredFields = clazz.getDeclaredFields(); for (Field f : declaredFields) { //1.权限修饰符 int modifiers = f.getModifiers(); System.out.print(Modifier.toString(modifiers)+"\t"); //2.数据类型 Class type = f.getType(); System.out.print(type.getName()+"\t"); //3.变量名 String fName = f.getName(); System.out.print(fName); System.out.println(); } } }

获取方法Method

public class MethodTest { @Test public void test1() { Class clazz = Person.class; //getMethods():获取当前运行时类及其所有父类中声明为public权限的方法 Method[] methods = clazz.getMethods(); for (Method m : methods) { System.out.println(m); } System.out.println("============"); //getDeclaredMethods():获取当前运行时类中声明的所有方法。(不包含父类中声明的方法) Method[] declaredMethods = clazz.getDeclaredMethods(); for (Method m : declaredMethods) { System.out.println(m); } } /* @Xxxx 权限修饰符 返回值类型 方法名(参数类型1 形参名1,...) throws XxxException{} */ @Test public void test2() throws ClassNotFoundException { Class clazz = Class.forName("cn.bruce.java1.Person"); Method[] declaredMethods = clazz.getDeclaredMethods(); for (Method m : declaredMethods) { //1.获取方法声明的注解 Annotation[] annos = m.getAnnotations(); for (Annotation a : annos) { System.out.println(a); } //2.权限修饰符 System.out.print(Modifier.toString(m.getModifiers())+"\t"); //3.返回值类型 System.out.print(m.getReturnType().getName() + "\t"); //4.方法名 System.out.print(m.getName()); System.out.print("("); //5.形参列表 Class[] parameterTypes = m.getParameterTypes(); if (!(parameterTypes == null && parameterTypes.length == 0)) { for (int i = 0; i < parameterTypes.length; i++) { if (i == parameterTypes.length - 1) { System.out.print(parameterTypes[i].getName() + " args_" + i); break; } System.out.print(parameterTypes[i].getName() + "args_" + i + ","); } } System.out.print(")"); //6.抛出的异常 Class[] exceptionTypes = m.getExceptionTypes(); if (exceptionTypes.length > 0){ System.out.print("throws "); for (int i = 0; i < exceptionTypes.length; i++) { if (i==exceptionTypes.length -1){ System.out.print(exceptionTypes[i].getName()); break; } System.out.print(exceptionTypes[i].getName()+","); } System.out.println(); } } } }

获取其他结构

public class OtherTest { /* 获取构造器结构 */ @Test public void test1() { Class clazz = Person.class; //getConstructors():获取当前运行时类中声明为public的构造器 Constructor[] constructors = clazz.getConstructors(); for (Constructor c : constructors) { System.out.println(c); } System.out.println("================"); //getDeclaredConstructors():获取当前运行时类中声明的所有的构造器 Constructor[] declaredConstructors = clazz.getDeclaredConstructors(); for (Constructor c : declaredConstructors) { System.out.println(c); } } /* 获取运行时类的父类 */ @Test public void test2(){ Class clazz = Person.class; Class...interface) 创建一个动态代理类所对应的Class对象

static Object newProxyInstance(ClassLoader loader, Class...interface, InvocationHandler h) 直接创建一个动态代理对象

4.4.3 动态代理实现步骤:

创建一个实现接口 InvocationHandler的类,它必须实现invoke()方法,以完成代理的具体操作。

创建被代理类以及接口

通过Proxy的静态方法newProxyInstance(ClassLoader loader, Class...interface, InvocationHandler h)创建一个接口代理

通过代理类的实例调用被代理类的方法

4.4.2 代码实现:

interface Human { String getBelief(); void eat(String food); } //被代理类 class SuperMan implements Human { @Override public String getBelief() { return "I believe I can fly!"; } @Override public void eat(String food) { System.out.println("I like eat " + food); } } /* 要想实现动态代理,需要解决的问题? 问题一:如何根据加载到内存中的被代理类,动态的创建一个代理类及其对象。 问题二:当通过代理类的对象调用方法a时,如何动态的去调用被代理类中的同名方法a。 */ //创建继承了InvocationHandler接口的类 class MyInvocationHanlder implements InvocationHandler { private Object obj;//需要使用被代理类的对象进行赋值 public void bind(Object obj) { this.obj = obj; } //当我们通过代理类的对象,调用方法a时,就会自动的调用如下的方法:invoke() //将被代理类要执行的方法a的功能就声明在invoke()中 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //method:即为代理类对象调用的方法,此方法也就作为了被代理类对象要调用的方法 //obj:被代理类的对象 Object returnValue = method.invoke(obj, args); //上述方法的返回值就作为当前类中的invoke()的返回值。 return returnValue; } } class ProxyFactory { //调用此方法,返回一个代理类的对象。解决问题一 public static Object getProxyInstance(Object obj) { MyInvocationHanlder hanlder = new MyInvocationHanlder(); hanlder.bind(obj); return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),hanlder); } } //测试动态代理 public class ProxyTest { public static void main(String[] args) { SuperMan superMan = new SuperMan(); //proxyInstance:代理类的对象 Human proxyInstance = (Human) ProxyFactory.getProxyInstance(superMan); //当通过代理类对象调用方法时,会自动的调用被代理类中同名的方法 String belief = proxyInstance.getBelief(); System.out.println(belief); proxyInstance.eat("火锅"); } }

5. 动态代理与AOP

使用 Proxy生成一个动态代理时,往往并不会凭空产生一个动态代理,这样没有太大的意义。通常都是为指定的目标对象生成动态代理。

这种动态代理在AOP中被称为AOP代理,AOP代理可代替目标对象,AOP代理包含了目标对象的全部方法。但AOP代理中的方法与目标对象的方法存在差异:

AOP代理里的方法可以在执行目标方法之前、之后插入一些通用处理

代码示例

//公共接口 interface Dog { void info(); void run(); } //被代理类 class HuntingDog implements Dog { @Override public void info() { System.out.println("我是一只猎狗"); } @Override public void run() { System.out.println("我跑的很快"); } } //通用方法 class DogUtils { public void method1() { System.out.println("=======通用方法一======="); } public void method2() { System.out.println("=======通用方法二======="); } } //动态代理实现 class MyInvocationHandler1 implements InvocationHandler { //需要被代理的对象 private Object target; public void SetTarget(Object target) { this.target = target; } //当我们通过代理类的对象,调用方法a时,就会自动的调用如下的方法:invoke() //将被代理类要执行的方法a的功能就声明在invoke()中 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { DogUtils dogUtils = new DogUtils(); //执行DogUtils对象中的method1 dogUtils.method1(); //通过obj对象来调用执行method方法 Object result = method.invoke(target, args); //执行DogUtils对象中的method2 dogUtils.method2(); return result; } } //动态代理类 class MyProxyFactory1 { //为target生成动态代理对象 public static Object getProxy(Object target) { //创建一个MyInvocationHandler对象 MyInvocationHandler1 handler = new MyInvocationHandler1(); //为MyInvocationHandler设置target对象 handler.SetTarget(target); //创建返回一个动态代理对象 return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), handler); } } public class AOPTest { public static void main(String[] args) { Dog target = new HuntingDog(); Dog dog = (Dog) MyProxyFactory1.getProxy(target); dog.info(); dog.run(); } }

我的理解

我认为反射应该是指通过对象或者名字来找到对应的类

反射是通过得到.class字节码文件 操作.java类中的内容

在Java中,每个class都有一个相应的Class对象。也就是说,当我们编写一个类,编译完成后,在生成的.class文件中,就会产生一个Class对象,用于表示这个类的类型信息。

类中有一个静态属性(类属性) 叫class,是一个Class类的实例

Class是类的类
类加载到内存中,类本身就作为Class的一个实例

根据Class.forName() 获取Class的一个实例,即 .class文件 运行时类,加载类

通过反射是可以获取到方法或者类的注解的 通过读取注解中的内容 根据不同的内容 我们可以让方法执行不同功能
比如注解内容为0 代表我们输出0

代理模式就是通过获得被代理类所实现的接口 然后创建代理对象也实现那个接口就完了

框架其实就是注解 加上反射 加上设计模式 三者构成框架

收藏
  • 人气文章
  • 最新文章
  • 下载排行榜
  • 热门排行榜