尚硅谷java学习笔记——13.java反射机制

来源:互联网 发布:2016淘宝客刷销量 编辑:程序博客网 时间:2024/05/19 13:57

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

Java反射机制提供的功能
在运行时判断任意一个对象所属的类
在运行时构造任意一个类的对象
在运行时判断任意一个类所具有的成员变量和方法
在运行时调用任意一个对象的成员变量和方法
生成动态代理

反射相关的主要API:
java.lang.Class:代表一个类
java.lang.reflect.Method:代表类的方法
java.lang.reflect.Field:代表类的成员变量
java.lang.reflect.Constructor:代表类的构造方法
。。。

一、Class 类

Object类中的
public final Class getClass()
方法被所有子类继承,返回值的类型是一个Class类,此类是Java反射的源头

这里写图片描述

对于每个类而言,JRE 都为其保留一个不变的 Class 类型的对象。一个 Class 对象包含了特定某个类的有关信息。

Class 对象只能由系统建立对象,一个Class对象对应的是一个加载到JVM中的一个.class文件,一个类在 JVM 中只会有一个Class实例

我们创建了一个类,通过编译(javac.exe),生成对应的 .class 文件,之后我们使用java.exe 加载(JVM的类加载器完成的)此 .class 文件,此 .class 文件加载到内存以后,就是一个运行时类,存在在缓存区。那么这个运行时类本身就是一个Class的实例!
1.每个运行时类只加载一次!
2.有了Class的实例以后,我们才能进行如下操作:
__①创建对应的运行时类的对象
__②获取对应的运行时类的完整结构(属性、方法、构造器、内部类、父类、所在的包、异常、注解……)
__③调用对应的运行时类指定的结构(属性、方法、构造器)
__④反射的应用:动态代理

Class类的常用方法

方法名 功能说明 static Class forName(String name) 返回指定类名 name 的 Class 对象 Object newInstance() 调用缺省构造函数,返回该Class对象的一个实例 getName() 返回此Class对象所表示的实体(类、接口、数组类、基本类型或void)名称 Class getSuperClass() 返回当前Class对象的父类的Class对象 Class [] getInterfaces() 获取当前Class对象的接口 ClassLoader getClassLoader() 返回该类的类加载器 Class getSuperclass() 返回表示此Class所表示的实体的超类的Class Constructor[] getConstructors() 返回一个包含某些Constructor对象的数组 Field[] getDeclaredFields() 返回Field对象的一个数组 Method getMethod(String name,Class … paramTypes) 返回一个Method对象,此对象的形参类型为paramType


如何获取Class的实例(3种)

@Test    public void test() throws ClassNotFoundException{        //1.调用运行时类本身的 .class 属性        Class clazz=Person.class;        System.out.println(clazz.getName());//day19.Person        Class clazz1=String.class;        System.out.println(clazz1.getName());//java.lang.String        //2.通过运行时类的对象获取        Person p=new Person();        Class clazz2=p.getClass();        System.out.println(clazz2.getName());//day19.Person        //3.通过Class的静态方法获取        String className="day19.Person";        Class clazz3=Class.forName(className);        System.out.println(clazz3.getName());//day19.Person        //4.(了解)通过类的加载器        ClassLoader classLoader=this.getClass().getClassLoader();        Class clazz4=classLoader.loadClass(className);        System.out.println(clazz4.getName());//day19.Person        System.out.println(clazz==clazz2);//true        System.out.println(clazz==clazz3);//true        System.out.println(clazz==clazz4);//true Person类就加载一次,获取的都是相同的Class类的实例    }

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

这里写图片描述

这里写图片描述

了解:ClassLoader
类加载器是用来把类(class)装载进内存的。JVM 规范定义了两种类型的类加载器:启动类加载器(bootstrap)和用户自定义加载器(user-defined class loader)。 JVM在运行时会产生3个类加载器组成的初始化加载器层次结构 ,如下图所示:
这里写图片描述

    @Test    public void test1() throws ClassNotFoundException, IOException{        //1.获取一个系统类加载器        ClassLoader classLoader=ClassLoader.getSystemClassLoader();        System.out.println(classLoader);//sun.misc.Launcher$AppClassLoader@6d06d69c        //2.获取系统类加载器的父类加载器,即扩展类加载器        classLoader=classLoader.getParent();        System.out.println(classLoader);//sun.misc.Launcher$ExtClassLoader@2503dbd3        //3.获取扩展类加载器的父类加载器,即引导类加载器        classLoader=classLoader.getParent();        System.out.println(classLoader);//null 引导类加载器是不能获取的        //4.测试当前类由哪个类加载器进行加载        classLoader=Class.forName("day19.TestReflection").getClassLoader();        System.out.println(classLoader);//sun.misc.Launcher$AppClassLoader@6d06d69c 是系统类加载器        //5.测试JDK提供的Object类由哪个类加载器加载        classLoader=Class.forName("java.lang.Object").getClassLoader();        System.out.println(classLoader);//null 说明是引导类加载器        //6.关于类加载器的一个主要方法:getResourceAsStream(String str):获取类路径下的指定文件的输入流        InputStream in=null;        in=this.getClass().getClassLoader().getResourceAsStream("jdbc.properties");        System.out.println(in);//java.io.BufferedInputStream@5b37e0d2        byte[] b=new byte[100];        in.read(b);        String str = new String(b);        System.out.println(str);//user=root 能获得文件内容    }

二、创建类对象并获取类的完整结构

有了Class对象,能做什么?

2.1.创建类的对象:

调用Class对象的newInstance()方法
要求: 1)类必须有一个无参数的构造器。 2)类的构造器的访问权限需要足够。

    @Test    public void test1() throws ClassNotFoundException, InstantiationException, IllegalAccessException{        String className="day19.Person";        Class clazz=Class.forName(className);        //创建对应的运行类的对象 要想创建成功,①要求对应的运行时类要有空参的构造器(类最好写一个空参的构造器)。②构造器的权限修饰符的权限要足够。        Object obj=clazz.newInstance();//实际调用的是Person空参的构造器        Person p=(Person) obj;        System.out.println(p);//Person [name=null, age=0]    }

难道没有无参的构造器就不能创建对象了吗?
不是!只要在操作的时候明确的调用类中的构造方法,并将参数传递进去之后,才可以实例化操作。步骤如下:
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);

2.2通过反射调用类的完整结构

1.实现的全部接口
public Class< ?>[] getInterfaces()
确定此对象所表示的类或接口实现的接口。

2.所继承的父类
public Class< ? Super T> getSuperclass()
返回表示此 Class 所表示的实体(类、接口、基本类型)的父类的 Class。

3.全部的构造器
public Constructor< T>[] getConstructors()
返回此 Class 对象所表示的类的所有public构造方法。
public Constructor< T>[] getDeclaredConstructors()
返回此 Class 对象表示的类声明的所有构造方法。

Constructor类中:
取得修饰符: public int getModifiers();
取得方法名称: public String getName();
取得参数的类型: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< T> annotationClass)
getDeclaredAnnotations()

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

8.类所在的包 Package getPackage()

public class TestOthers {    //6.获取注解    public void test6(){        Class clazz=Person.class;        Annotation[] anns=clazz.getAnnotations();        for(Annotation a:anns){            System.out.println(a);        }    }    //5.获取所在的包    @Test    public void test5(){        Class clazz=Person.class;        Package pack=clazz.getPackage();        System.out.println(pack);    }    //4.获取实现的接口    @Test    public void test4(){        Class clazz=Person.class;        Class[] interfaces=clazz.getInterfaces();        for(Class i:interfaces){            System.out.println(i);        }    }    //3.获取父类的泛型    @Test    public void test3(){        Class clazz=Person.class;        Type type1=clazz.getGenericSuperclass();        ParameterizedType param=(ParameterizedType) type1;        Type[] ars=param.getActualTypeArguments();        for(Type type:ars)            System.out.println(((Class)type).getName());    }    //2.获取带泛型的父类    @Test    public void test2(){        Class clazz=Person.class;        Type type1=clazz.getGenericSuperclass();        System.out.println(type1);    }    //1.获取运行时类的父类    @Test    public void test1(){        Class clazz=Person.class;        Class superclass=clazz.getSuperclass();        System.out.println(superclass);    }}
public class TestMethod {    //1.获取运行时类的方法    @Test    public void test1(){        Class clazz=Person.class;        //1.getMethods():获取运行时类及其父类中所有的声明为public的方法        Method[] m1=clazz.getMethods();        for(Method m:m1){            System.out.println(m);        }        //2.getDeclaredMethods():获取运行时类本身声明的所有的方法        Method[] m2=clazz.getDeclaredMethods();        for(Method m : m2){            System.out.println(m);        }    }    //注解  权限修饰符 返回值类型 方法名 形参列表 异常    @Test    public void test2(){        Class clazz=Person.class;        Method[] m2=clazz.getDeclaredMethods();        for(Method m : m2){            //1.注解            Annotation[] ann=m.getAnnotations();            for(Annotation a:ann){                System.out.println(a);            }            //2.权限修饰符            String str=Modifier.toString(m.getModifiers());            System.out.print(str+" ");            //3.返回值类型            Class returnType=m.getReturnType();            System.out.print(returnType.getName()+" ");            //4.方法名            System.out.print(m.getName()+" ");            //5.形参列表            System.out.print("(");            Class[] params=m.getParameterTypes();            for(int i=0;i<params.length;i++){                System.out.print(params[i].getName()+" args-"+i+" ");            }            System.out.print(")");            //6.异常类型            Class[] exps=m.getExceptionTypes();            if(exps.length!=0) System.out.print("throws ");            for(int i=0;i<exps.length;i++){                System.out.print(exps[i].getName());            }            System.out.println();        }    }}
public class TestField {    //获取运行时类的属性    @Test    public void test1(){        Class clazz=Person.class;        //1.getFields():只能获取到运行时类中及其父类中声明为public属性        Field[] fields=clazz.getFields();        for(int i=0;i<fields.length;i++){             System.out.println(fields[i]);        }//public java.lang.String day19.Person.name        //2.getDeclaredFields()获取运行时类本身声明的所有属性(不能获取父类属性)        Field[] fields1=clazz.getDeclaredFields();        for(Field f:fields1){            System.out.println(f.getName());            System.out.println();        }//name  age    }    //权限修饰符  变量类型  变量名    //获取属性的各个部分的内容    public static void test2(){        Class clazz=Person.class;        Field[] fields1=clazz.getDeclaredFields();        for(Field f:fields1){            //1.获取每个属性的权限修饰符            int i=f.getModifiers();            String str1=Modifier.toString(i);            System.out.print(str1+" ");            //2.获取属性的变量类型            Class type=f.getType();            System.out.print(type.getName()+" ");            //3.获取属性名            System.out.print(f.getName());            System.out.println();        }    }}
public class TestConstructor {    @Test    public void test1() throws ClassNotFoundException, InstantiationException, IllegalAccessException{        String className="day19.Person";        Class clazz=Class.forName(className);        //创建对应的运行类的对象 要想创建成功,①要求对应的运行时类要有空参的构造器(类最好写一个空参的构造器)。②构造器的权限修饰符的权限要足够。        Object obj=clazz.newInstance();//实际调用的是Person空参的构造器        Person p=(Person) obj;        System.out.println(p);//Person [name=null, age=0]    }    @Test    public void test2() throws ClassNotFoundException{        Class clazz=Class.forName("day19.Person");        Constructor[] cons=clazz.getDeclaredConstructors();        for(Constructor c:cons){            System.out.println(c);        }    }}

三、通过反射调用类中的指定方法、指定属性

//调用运行时类中指定的属性    @Test    public void test3() throws NoSuchFieldException, SecurityException, InstantiationException, IllegalAccessException{        Class clazz=Person.class;        //1.获取指定的属性        //getField(String fieldName):获取运行时类中声明为public的指定属性名为fieldName的属性        Field name=clazz.getField("name");        //2.创建运行时类的对象        Person p=(Person) clazz.newInstance();        System.out.println(p);        //3.将运行时类的指定的属性赋值        name.set(p, "Jerry");        System.out.println(p);        System.out.println();//      Field age=clazz.getField("age");//私有类型的不可见        //getDeclaredField(String fieldName):获取运行时类中指定的名为fieldName的属性        Field age=clazz.getDeclaredField("age");        //由于属性权限修饰符的限制,为了保证可以给属性赋值,需要在操作前使得此属性可被操作。        age.setAccessible(true);//不然会出 illegalAccessException        age.set(p, 10);        System.out.println(p);    }
//调用运行时类中指定的方法    @Test    public void test3() throws Exception{        Class clazz=Person.class;        //getMethod(String methodName,Class ...params):获取运行时类中声明为public的指定的方法        Method m1=clazz.getMethod("show");        Person p=(Person) clazz.newInstance();        //调用指定的方法:Object invoke(Object obj,Object ...obj)        Object returnVal=m1.invoke(p);        System.out.println(returnVal);        //对于运行时类中静态方法的调用        Method m3=clazz.getMethod("info");        m3.invoke(Person.class);        //getDeclaredMethod(String methodName,Class ... params):获取运行时类中        Method m4=clazz.getDeclaredMethod("diaplay",String.class,Integer.class);        m4.setAccessible(true);        Object value=m4.invoke(p, "CHN",10);        System.out.println(value);    }
//调用指定的构造器,创建运行时类的对象        @Test        public void test3() throws Exception{            String className="day19.Person";            Class clazz=Class.forName(className);            Constructor cons=clazz.getDeclaredConstructor(String.class,int.class);            cons.setAccessible(true);            Person p=(Person) cons.newInstance("zxm",20);            System.out.println(p);         }

Object invoke(Object obj, Object … args)
说明:
1.Object 对应原方法的返回值,若原方法无返回值,此时返回null
2.若原方法若为静态方法,此时形参Object obj可为null
3.若原方法形参列表为空,则Object[] args为null
4.若原方法声明为private,则需要在调用此invoke()方法前,显式调用方法对象的setAccessible(true)方法,将可访问private的方法。

2.调用指定属性
在反射机制中,可以直接通过Field类操作类中的属性,通过Field类提供的set()和get()方法就可以完成设置和取得属性内容的操作。
public Field getField(String name) 返回此Class对象表示的类或接口的指定的public的Field。
public Field getDeclaredField(String name)返回此Class对象表示的类或接口的指定的Field。

在Field中:
public Object get(Object obj) 取得指定对象obj上此Field的属性内容
public void set(Object obj,Object value) 设置指定对象obj上此Field的属性内容

注:在类中属性都设置为private的前提下,在使用set()和get()方法时,首先要使用Field类中的setAccessible(true)方法将需要操作的属性设置为可以被外部访问。
public void setAccessible(true)访问私有属性时,让这个属性可见。

Java动态代理

动态代理是指客户通过代理类来调用其它对象的方法,并且是在程序运行时根据需要动态创建目标类的代理对象。

动态代理使用场合:
- 调试
- 远程方法调用

代理设计模式的原理:
使用一个代理将对象包装起来, 然后用该代理对象取代原始对象. 任何对原始对象的调用都要通过代理. 代理对象决定是否以及何时将方法调用转到原始对象上

静态代理:

//静态代理模式//接口interface ClothFactory{    void productCloth();}//被代理类class NikeClothFacoty implements ClothFactory{    @Override    public void productCloth() {        System.out.println("Nike工厂生产一批衣服");    }}//代理类class ProxyFactory implements ClothFactory{    ClothFactory cf;    //创建代理类的对象时,实际传入被代理类的对象    public ProxyFactory(ClothFactory cf){        this.cf=cf;    }    @Override    public void productCloth() {        System.out.println("代理类开始执行,收代理费¥1000");        cf.productCloth();    }}public class TestClothProduct {    public static void main(String[] args) {        NikeClothFacoty nike=new NikeClothFacoty();//创建被代理类的对象        ProxyFactory proxyFactory=new ProxyFactory(nike);//创建代理类的对象        proxyFactory.productCloth();    }}
//动态代理interface Subject{    void action();}//被代理类class RealSubject implements Subject{    @Override    public void action() {        System.out.println("我是被代理类,记得执行我哦!");    }}//用来动态创建代理类class MyInvocationHandler implements InvocationHandler{    Object obj;//实现了接口的被代理类的对象的声明    //①给被代理的对象实例化②返回一个代理类的对象    public Object blind(Object obj){        this.obj=obj;        return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), this);//类加载器,得到全部的接口,得到InvocationHandler接口的子类实例    }    //当通过代理类的对象发起对被重写的方法的调用时,都会转换为对如下的invoke方法的调用    @Override    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {//被代理的对象,要调用的方法,方法调用时所需要的参数        //method方法的返回值是returnVal        Object returnVal=method.invoke(obj, args);        return returnVal;    }}public class TestProxy {    public static void main(String[] args) {        //1.被代理类的对象        RealSubject real=new RealSubject();        //2.创建一个实现了 InvacationHandler接口的类的对象        MyInvocationHandler handler=new MyInvocationHandler();        //3.调用blind()方法,动态的返回一个同样实现了real所在类实现的接口Subject的代理类的对象。        Object obj=handler.blind(real);        Subject sub=(Subject) obj;//此时sub就是代理类的对象        sub.action();//转到对InvacationHandler接口的实现类的invoke()方法的调用        //再举一例        NikeClothFacoty nike=new NikeClothFacoty();        ClothFactory proxyCloth=(ClothFactory) handler.blind(nike);//proxyCloth即为代理类的对象        proxyCloth.productCloth();    }}

动态代理与AOP(Aspect Orient Programming)

interface Dog{    void info();    void run();}class HuntingDog implements Dog{    public void info(){        System.out.println("我是一只猎狗");    }    public void run(){        System.out.println("我奔跑迅速");    }}class DogUtil{    public void method1(){        System.out.println("=====模拟通用方法一=====");    }    public void method2(){        System.out.println("=====模拟通用方法二=====");    }}class MyInvocationHandler implements InvocationHandler {    // 需要被代理的对象    private Object target;    public void setTarget(Object target) {        this.target = target;    }    // 执行动态代理对象的所有方法时,都会被替换成执行如下的invoke方法    public Object invoke(Object proxy, Method method, Object[] args) throws Exception {        DogUtil du = new DogUtil();        // 执行DogUtil对象中的method1。        du.method1();        // 以target作为主调来执行method方法        Object result = method.invoke(target, args);        // 执行DogUtil对象中的method2。        du.method2();        return result;    }}class MyProxyFactory{    // 为指定target生成动态代理对象    public static Object getProxy(Object target)        throws Exception{        // 创建一个MyInvokationHandler对象        MyInvocationHandler handler =         new MyInvocationHandler();        // 为MyInvokationHandler设置target对象        handler.setTarget(target);        // 创建、并返回一个动态代理对象        return Proxy.newProxyInstance(target.getClass().getClassLoader()            , target.getClass().getInterfaces() , handler);    }}public class Test {    public static void main(String[] args)             throws Exception{            // 创建一个原始的HuntingDog对象,作为target            Dog target = new HuntingDog();            // 以指定的target来创建动态代理            Dog dog = (Dog)MyProxyFactory.getProxy(target);            dog.info();            System.out.println();            dog.run();        }}

执行结果:
这里写图片描述

使用Proxy生成一个动态代理时,往往并不会凭空产生一个动态代理,这样没有太大的意义。通常都是为指定的目标对象生成动态代理
这种动态代理在AOP中被称为AOP代理,AOP代理可代替目标对象,AOP代理包含了目标对象的全部方法。但AOP代理中的方法与目标对象的方法存在差异:AOP代理里的方法可以在执行目标方法之前、之后插入一些通用处理

这里写图片描述

0 0