JavaSE:反射总结(Reflection)

今天来进行反射的总结,在JavaSE应用的几个部分里,我认为反射是非常重要的一个内容,因为我们在后续学习框架,以及编程思想上的理解都离不开它。内容不是很多,但是需要理解。

1.为什么要有反射?

某种情况下,我们需要在运行时才得知并使用一个编译时完全未知的类,创建其对象,调用其方法和属性。

2.反射:

被视为动态语言的关键,允许程序在执行期间借助Reflection API取得任何类的内部信息,并直接操纵任意对象的内部属性和方法。

3.类加载的过程:

当程序主动使用某个类时,如果该类还未被加载到内存,系统会通过以下三个步骤来对类进行初始化。

①类的加载:

将.class文件读入到内存中,并为之创建一个java.lang.Class对象。有类加载器来完成

②类的连接:

将类的二进制数据合并到JRE中。

③类的初始化:

JVM负责对类进行初始化。

扩展:

类加载的特性:

在虚拟机生命周期中一个类只被加载一次

加载原则:延迟加载,能少加载就少加载,因为虚拟机的空间是有限的

类加载的时机:

1)第一次创建对象时

2)调用静态方法时要加载类,访问静态属性时会加载类

3)加载子类必先加载父类

4)创建对象引用不加载类

5)子类调用父类的静态方法时:

①当子类没有覆盖父类的静态方法时 ,只加载父类,不加载子类

②当子类覆盖父类的静态方法时,加载父类, 也加载子类

6)访问静态常量,如果编译器可以直接计算出常量的值,则不会加载类,负责加载类。

4.获取Class类对象的实例:

Class clazz = Person.class();

Person p = new Person();
Class clazz = p.getClass();

String className = "com.qh.review.Person";
Class clazz = Class.forName(className);

String className = "com.qh.review.Person";
ClassLoader cl = Person.class.getClassLoader();
class clazz = cl.loadClass(className );

5.类加载器

①获取系统类加载器

ClassLoader cl = ClassLoader.getSystemClassLoader();
System.out.println(cl);

注意:负责加载classpath下的所有jar包

②获取扩展类加载器

ClassLoader cl2 = cl.getParent();
System.out.println(cl2);

注意:负责加载jre/lib/ext下的所有jar包

ClassLoader cl3 = cl2.getParent();
System.out.println(cl3);

注意:负责加载核心类库,无法直接获取

获取当前类由哪个类来加载:

ClassLoader cl = Person.class.ClassLoader();
System.out.println(cl);

6.通过反射来获取对象实例

Person person = Person.class().newInstance();

注意: 这里这个newInstance()实际上调用的是类的构造器。可以通过声明一个带参的构造器来测试。

7.操作属性,这里的代码,我就把我今天回顾的内容粘过来了,大家不想看的可以往下跳过。想看的可作一个参考。

// 操作属性
    @Test
    public void test4() {
        // 先获取具体类的Class对象
        Class<?> clazz = null;
        try {
            clazz = Class.forName("com.qh.review2.Person");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        // 通过clazz来获取类属性---->public 1,protected 4, private 2, default 0
        Field[] fields = clazz.getFields();// 只能获取访问控制修饰符为public的属性.访问控制修饰符为1即为public
        for (Field field : fields) {
            System.out.println(field.getName());// phone
            System.out.println(field.getType());// 1
            System.out.println(field.getModifiers()); // class java.lang.String
        }
        System.out.println("---------------------------------------------");
        Field[] fields2 = clazz.getDeclaredFields();
        for (Field field : fields2) {
            System.out.println(field.getName());
            System.out.println(field.getModifiers());
            System.out.println(field.getType());
            System.out.println("************");
        }
        // 对某一对象的属性进行操作:一个类确定后,就不能修改其修饰符,数据类型,名称。同时操作私有属性的时候,需要修改访问权限。
        Field name = null;
        Person person = null;

        try {
            person = (Person) clazz.newInstance();
            name = clazz.getDeclaredField("name");
            name.setAccessible(true);
            name.set(person, "张三");
        } catch (InstantiationException | IllegalAccessException
                | NoSuchFieldException | SecurityException e) {
            e.printStackTrace();
        }
        System.out.println(person);
    }

8.操作方法

@Test
    public void test5() {
        // 获取所有方法--->方法的修饰符 返回的数值不确定。??
        Method[] methods = clazz.getDeclaredMethods();
        for (int i = 0; i < methods.length; i++) {
            System.out.println("访问控制修饰符:" + methods[i].getModifiers());
            System.out.println("返回值类型:" + methods[i].getReturnType());
            System.out.println("方法名:" + methods[i].getName());
            Class<?>[] parameterType = methods[i].getParameterTypes();
            for (Object paramObject : parameterType) {
                System.out.println("参数类型:" + paramObject);
            }
            Class<?>[] exceptionTypes = methods[i].getExceptionTypes();
            for(Object eType : exceptionTypes){
                System.out.println("异常类型:" + eType);
            }
            Type[] grnericTypes =  methods[i].getGenericParameterTypes();
            for(Type type : grnericTypes){
                System.out.println("泛型类型:" + type);
            }
            System.out.println("------------------------------------------");
        }
        // 操作方法:--->方法只能调用。
        System.out.println("操作方法");
        try {
            Person person = clazz.newInstance();
            Method getName = clazz.getDeclaredMethod("getName");

            System.out.println(getName.invoke(person));
        } catch (InstantiationException | IllegalAccessException
                | NoSuchMethodException | SecurityException
                | IllegalArgumentException | InvocationTargetException e) {
            e.printStackTrace();
        }
    }

我把获取类对象提到外面了,要不每次还得写。

重点: 这里一定要注意方法的调用。后面的动态代理和AOP要用到。

8.获取父类泛型类型,感觉这个还是蛮重要的。前段时间我写的jdbc中就用到。我还是写一写吧。

Class clazz = Person.class();
Type type = clazz.getGenericSuperClass();
ParameterizedType types = (ParameterizedType)type;
for(Type t : types){
    System.out.println(t);
}

这些东西都要熟记,给一个记事本就能写出来,并且别出错。我就是这样要求我的。

9.还有一些类型,这里我就不说了,大家可以参看关于Reflection的API文档

接下来写三个应用:

在写动态代理之前,我们先回顾一下静态代理怎么写?

1.静态代理

代理类和被代理类实现同一个接口,在代理类中通过代理类对象调用接口中的方法,但实际上还是通过被代理类对象调用方法来完成相应的功能。

汗,竟然没我上午总结的好,来看看。

一个接口,一个被代理类,一个代理类,代理类和被代理类都可以实现接口中的方法

被代理类通过代理类来执行代理类中的方法。

interface Factory{
    void produce();
}
//被代理类
class NikeFactory implements Factory{
    public void produce(){
        System.out.println("生产NIKE衣服。。");
    }
}
//代理类
class NikeProxy implements Factory{
    NikeFactory nf = null;//实际上要调用方法的代理类对象
    public NikeProxy(NikeFactory nf){
        this.nf = nf;//初始化这个对象
    }
    public void produce(){
        nf.produce();//实际上是代理类对象来生产的衣服
    }
}

public class Proxy{
    public void main(String[] args){
        NikeFactory nf = new NikeFactory();
        NikeProxy np = new NikeProxy(nf);
        np.produce();//生产NIKE衣服。。
    }
}

懂了么?实际上动态代理和AOP逻辑上和这个是一样的。只不过静态代理只能是针对特定的对象。

2.动态代理:

interface Factory{//还是和刚刚一样
    void produce();
}

class NikeFactory implements Factory{//还是和刚刚一样
    public void produce(){
        System.out.println("生产NIKE衣服。。");
    }
}
//来看代理类
class MyInvocationHandler implements InvocationHandler{
    Object obj = null;//还是这个代理类对象
    //需要一个代理类对象,当然是通过反射的方式获取喽!
    public Object getProxy(Object obj){
        this.obj = obj;//初始化一下
        return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(), this);
    }
    //实现的方法没记住,粘一下。
    //代理类执行被代理类方法时,转换为对此方法的调用。
    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {//还记得我上面通过反射操作方法中的注意事项吗?回头看看吧。

        return method.invoke(obj, args);//同上,要不你这里理解不了。
    }
}

public class TestProxy{
    public void main(String[] args){
        NikeFactory nf = new NikeFactory();
        MyInvocationHandler mih = new MyInvocationHandler();
        Object obj = mih.getProxy(nf);//返回的代理类对象
        Factory f = (Factory)obj;//实现了Factory接口
        f.produce();//是不是在生产NIKE的衣服?
    }
}

3.AOP(Aspect Oriented Programming)面向切面编程。到现在为止,这是我见到的第一个面向切面编程的例子,可能只是掌握了其形式,对其内涵还没有理解。会随着以后的学习增加对其的理解。

来看一张图:

其实具体的内容和动态代理没什么两样。代码就不写了。给大家粘出来看看。

interface Human {
    void walk();

    void fly();
}

class SuperMan implements Human {

    @Override
    public void walk() {
        System.out.println("走走走走走啊走");
    }

    @Override
    public void fly() {
        System.out.println("I belive I can fly");
    }

}

class HuManUtil {
    public void method1() {
        System.out.println("method 1  !!!!");
    }

    public void method2() {
        System.out.println("method 2  !!!!");
    }
}

// 获取代理对象
class ManInvocationHandler implements InvocationHandler {
    Object obj;

    public Object getProxy(Object obj) {
        this.obj = obj;
        return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj
                .getClass().getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        HuManUtil h1 = new HuManUtil();
        h1.method1();
        Object returnVal = method.invoke(obj, args);
        h1.method2();
        return returnVal;
    }

}

public class TestAOP {
    public static void main(String[] args) {
        SuperMan sm = new SuperMan();
        ManInvocationHandler mih = new ManInvocationHandler();
        Object obj = mih.getProxy(sm);
        Human proxy = (Human) obj;
        proxy.walk();
        System.out.println();
        proxy.fly();

    }
}
  • 多个代码段中有相同重复的代码,我们将这些重复的代码提取出来,
  • 封装成方法,但是这个方法就是固定的,增加了与多段代码的耦合性。
  • 我们理想的方式就是封装出来的方法是不固定的,动态的,
  • 这样就大大降低了代码之间的耦合性,代码之间的依赖性。

    这是我现在理解的面向切面编程。

    好了,今天就到这里了。

时间: 2024-08-15 14:10:29

JavaSE:反射总结(Reflection)的相关文章

Java反射(Reflection)

基本概念 在Java运行时环境中,对于任意一个类,能否知道这个类有哪些属性和方法?对于任意一个对象,能否调用它的任意一个方法? 答案是肯定的. 这种动态获取类的信息以及动态调用对象的方法的功能来自于Java语言的反射(Reflection)机制. Java反射机制主要提供了以下功能: 1.在运行时判断任意一个对象所属的类. 2.在运行时构造任意一个类的对象. 3.在运行时判断任意一个类所具有的成员变量和方法. 4.在运行时调用任意一个对象的方法. 5.在运行时设定任意一个对象的属性值. Refl

Swift - 反射(Reflection)的介绍与使用样例(附KVC介绍)

1,反射(Reflection) 对于C#.Java开发人员来说,肯定都对反射这个概念相当熟悉.所谓反射就是可以动态获取类型.成员信息,同时在运行时(而非编译时)可以动态调用任意方法.属性等行为的特性. 以Java上的两个知名框架(hibernate和spring)为例.hibernate的属性映射就是通过反射来赋值的,spring的bean的创建就是根据配置的class来反射构建的. 2,Objective-C 的 Runtime在使用ObjC开发时很少强调其反射概念,因为ObjC的Runti

C# 反射(Reflection)

什么是反射 发射是 .net framework 提供的一个帮助类库,用于读取和使用元数据. 用到的类:System.Reflection,System.Type.System.Type 类对于反射起着核心的作用. 当反射请求加载的类型时,公共语言运行时将为它创建一个 Type. 您可以使用 Type 对象的方法.字段.属性和嵌套类来查找有关该类型的所有信息. 反射优缺点 优点: 1.反射提高了程序的灵活性和扩展性. 2.降低耦合性,提高自适应能力. 3.它允许程序创建和控制任何类的对象,无需提

【Unity|C#】基础篇(12)——反射(Reflection)(核心类:Type、Assembly)

[学习资料] <C#图解教程>(第24章):https://www.cnblogs.com/moonache/p/7687551.html 电子书下载:https://pan.baidu.com/s/1mhOmBG0 [内容] 对以下文章的整合: 详解C#中的反射(主要参考):https://www.cnblogs.com/jiangyunfeng/p/10436520.html C#反射机制                       :https://zhuanlan.zhihu.com/

[javaSE] 反射-方法的反射

1.如何获取某个方法 方法的名称和方法的参数列表才能唯一决定一个方法 2.方法反射的操作 method.invoke(); package com.tsh.reflect; import java.lang.reflect.Method; public class ReflectDemo { public static void main(String[] args) { P p=new P(); Class c=P.class; try { Method method=c.getDeclare

JavaSE——反射机制

1.反射是什么? 2.通过反射获取类的Class对象 3.什么时候会发生类的初始化 4.类加载器 5.反射的一些方法 6.通过反射获取类的运行时结构 1.反射是什么? 动态语言在程序运行时可改变其结构,反射是java被视为动态语言的关键. 2.通过反射获取类的Class对象 2.1. Class c1 = Class.forName ("包名+类名") ; 2.2. Class c2 =对象.getClass(); . 2.3. Class c3 =类名.class; 注意: 1.一个

[javaSE] 反射-动态加载类

Class.forName(“类的全称”) ①不仅表示了类的类类型,还代表了动态加载类 ②请大家区分编译,运行 ③编译时刻加载类是静态加载类,运行时刻加载类是动态加载类 Ⅰ所有的new对象都是静态加载类 在编译的时刻就要去检测该类是否存在,如果不存在,编译失败. //对于这种情况,静态加载不适用,因为我们需要根据输入来确定加载哪个类 package com.tsh.reflect; class ReflectLoadDemo { public static void main(String[]

Java进阶之reflection(反射机制)——反射概念与基础

反射机制是Java动态性之一,而说到动态性首先得了解动态语言.那么何为动态语言? 一.动态语言 动态语言,是指程序在运行时可以改变其结构:新的函数可以引进,已有的函数可以被删除等结构上的变化.比如常见的JavaScript就是动态语言,除此之外Ruby,Python等也属于动态语言,而C.C++则不属于动态语言. 二.Java是动态语言吗? 从动态语言能在运行时改变程序结构结构或则变量类型上看,Java和C.C++一样都不属于动态语言. 但是JAVA却又一个非常突出的与动态相关的机制:反射机制.

什么是反射?反射有什么用处

什么是反射?反射有什么用处? 1. 什么是反射? “反射(Reflection)能够让运行于JVM中的程序检测和修改运行时的行为.”这个概念常常会和内省(Introspection)混淆,以下是这两个术语在Wikipedia中的解释: 内省用于在运行时检测某个对象的类型和其包含的属性: 反射用于在运行时检测和修改某个对象的结构及其行为. 从它们的定义可以看出,内省是反射的一个子集.有些语言支持内省,但并不支持反射,如C++. 内省示例:instanceof 运算符用于检测某个对象是否属于特定的类