尚硅谷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类的常用方法
如何获取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代理里的方法可以在执行目标方法之前、之后插入一些通用处理
- 尚硅谷java学习笔记——13.java反射机制
- 尚硅谷java学习笔记——11.java多线程
- 尚硅谷java学习笔记——2.基本语法
- 尚硅谷java学习笔记——6.异常处理
- 尚硅谷java学习笔记——14.网络编程
- 尚硅谷Java基础学习笔记一
- 尚硅谷Java基础学习笔记二
- 尚硅谷Java基础学习笔记三
- 尚硅谷Java基础学习笔记四
- 尚硅谷Java基础学习笔记五
- 尚硅谷java学习笔记——1.java语言概述
- 尚硅谷java学习笔记——8.java泛型(Generic)
- 尚硅谷java学习笔记——9.java枚举&注解
- 尚硅谷java学习笔记——10.java IO流
- 尚硅谷java学习笔记——12.java常用类
- 尚硅谷java学习笔记——JUC(java.util.concurrent)
- 尚硅谷Java基础笔记
- 尚硅谷Java学习-入门
- 50个很棒的python模块
- Android Studio如何导入第三方主题
- 在android 下支持ntfs-3g
- 1004. 成绩排名 (20)
- (Java学习笔记5.1)简单排序
- 尚硅谷java学习笔记——13.java反射机制
- 详解各类球面投影
- MAVEN安装配置
- 基于voidAR实现增强现实之初音未来
- iOS逆向之“修改微信运动步数”
- 图片解码播放器
- js几种实用的跨域方法和原理
- 函数,const指针参数,递归,指向函数的指针
- NoSQL--键值