15-1 Java反射机制概述
一、 Java反射机制提供的功能
在运行时判断任意一个对象所属的类
在运行时构造任意一个类的对象
在运行时判断任意一个类所具有的成员变量和方法
在运行时获取泛型信息
在运行时调用任意一个对象的成员变量和方法
在运行时处理注解
生成动态代理
二、 反射相关的主要API
15-2 理解Class类并 获取Class的实例
一、Class类API
二、实现
//反射之前对于Person的操作 @Test public void test1(){ //1.创建Person类的对象 Person p1 = new Person("Tom",20); //2.通过对象调用其内部的属性和方法 p1.age = 10; System.out.println(p1.toString()); p1.show(); //在Person类外部不可以通过Person类对象调用其内部私有结构 //比如:name、showNation()、以及私有的构造器 } //反射之后对于Person的操作 @Test public void test2() throws Exception{ Class clazz = Person.class; //1.通过反射,创建Person类的对象 Constructor cons = clazz.getConstructor(String.class, int.class); Object obj = cons.newInstance("Tom", 12); Person p = (Person)obj; System.out.println(obj.toString()); //2.通过反射,调用对象指定的属性、方法 //调用属性 Field age = clazz.getDeclaredField("age"); age.set(p,10); System.out.println(obj.toString()); //调用方法 Method show = clazz.getDeclaredMethod("show"); show.invoke(p); //通过反射,可以调用Person类的私有结构的。比如:私有的构造器、方法、属性 //调用私有的构造器 Constructor declaredConstructor = clazz.getDeclaredConstructor(String.class); declaredConstructor.setAccessible(true); Person p1 = (Person)declaredConstructor.newInstance("Jerry"); System.out.println(p1); //调用私有的属性和方法 Field name = clazz.getDeclaredField("name"); name.setAccessible(true); name.set(p1,"Mbappe"); System.out.println(p1); //调用私有的方法 Method showNation = clazz.getDeclaredMethod("showNation", String.class); showNation.setAccessible(true); String nation = (String) showNation.invoke(p1,"中国");//相当于p1.showNation("中国"); System.out.println(nation); }
三、理解
1.通过直接new的方式或反射的方式都可以调用公共的结构,开发中到底用哪个?
建议:直接new的方式。
什么时候会使用:反射的方式。反射的特征:动态性
2.反射机制于面向对象中的封装性是不是矛盾的?如何看待两个技术?
不矛盾。
3.关于java.lang.Class类的理解
1.类的加载过程:
程序在经过javac.exe命令后,会生成一个或多个字节码文件,接着我们使用java.exe命令对某个字节码文件进行解释运行。相当于将某个字节文件加载到内存中。此过程就成为类的加载。加载到内存中的类,我们就成为运行时类,此运行时类,就作为Class的一个实例。
2.换句话说,Class的实例就对应着一个运行时类
3.加载到内存中的运行时类,会缓存一定的时间。在此时间之内,我们可以通过不同的方式来获取此运行时类
三、获取Class实例的方式
//获取Class实例的方式 @Test public void test3() throws ClassNotFoundException { //方式一:调用运行时类的属性:.class Class<Person> clazz1 = Person.class; System.out.println(clazz1); //方式二:通过运行时类的对象,调用getClass() Person p1 = new Person(); Class clazz2 = p1.getClass(); System.out.println(clazz2); //方式三:调用Class的静态方法:forName(String classPath) Class<?> clazz3 = Class.forName("com.atguigu.java.Person"); System.out.println(clazz3); System.out.println(clazz1 == clazz2); System.out.println(clazz2 == clazz3); //方式四:使用类的加载器:ClassLoader ClassLoader classLoader = ReflectionTest.class.getClassLoader(); Class<?> clazz4 = classLoader.loadClass("com.atguigu.java.Person"); System.out.println(clazz4); System.out.println(clazz3 == clazz4); }
四、拥有Class对象的类型
、
注意: 只要数组元素类型与维度一样,就是同一个Class
15-3 类的加载 与ClassLoader的理解
一、理解类的加载过程
二、理解实现
@Test public void test1(){ //对于自定义类,使用系统类加载器进行加载 ClassLoader classLoader = ClassLoaderTest.class.getClassLoader(); System.out.println(classLoader); //调用系统类加载的getParent(),获取扩展类加载器 ClassLoader classLoader1 = classLoader.getParent(); System.out.println(classLoader1); //调用扩展类加载器的getParent(),无法获取引导类加载器 //引导类加载器主要负责加载java的核心类库,无法加载自定义类的 ClassLoader classLoader2 = classLoader1.getParent(); System.out.println(classLoader2); }
三、利用反射读取配置文件
/* * Properties:用来读取配置文件 * */ @Test public void test2() throws IOException { Properties pros = new Properties(); //此时的文件默认在当前的module下 //读取配置文件的方式一: // FileInputStream fis = new FileInputStream("src\\jdbc1.properties"); // pros.load(fis); //读取配置文件的方式二:使用ClassLoader //配置文件默认识别为:当前module的src下 ClassLoader classLoader = ClassLoaderTest.class.getClassLoader(); InputStream resourceAsStream = classLoader.getResourceAsStream("jdbc1.properties"); pros.load(resourceAsStream); String user = pros.getProperty("user"); String password = pros.getProperty("password"); System.out.println("user = " + user + ",password = " + password); }
15 - 4 创建运行时类的对象
一、创建方式(前置内容见15-2 三)
newInstance():调用此方法,创建对应的运行时类的对象。内部调用了运行时类的空参构造器
要想此方法正常的创建运行时类的对象,要求:
1.运行时类必须提供空参的构造器
2.空参的构造器的访问权限得够。通常设置为public。
在javabean中要求提供一个public的空参构造器。原因:
1.便于通过放射,创建运行时类的对象
2.便于子类继承此运行时类是,默认调用super()是,保证父类有此构造器。
二、对于运行时类需要提供空参构造器的理解
难道没有无参的构造器就不能创建对象了吗?
不是!只要在操作的时候明确的调用类中的构造器,并将参数传递进去之后,才可以实例化操作。 步骤如下:
1)通过Class类的getDeclaredConstructor(Class … parameterTypes)取得本类的指定形参类 型的构造器
2)向构造器的形参中传递一个对象数组进去,里面包含了构造器中所需的各个参数。 3)通过Constructor实例化对象。
@Test public void test1() throws Exception{ Class<Person> clazz = Person.class; /* * newInstance():调用此方法,创建对应的运行时类的对象。内部调用了运行时类的空参构造器 * 要想此方法正常的创建运行时类的对象,要求: * 1.运行时类必须提供空参的构造器 * 2.空参的构造器的访问权限得够。通常设置为public。 * * 在javabean中要求提供一个public的空参构造器。原因: * 1.便于通过反射,创建运行时类的对象 * 2.便于子类继承此运行时类时,默认调用super(),保证父类有此构造器。 * */ Person person = clazz.newInstance(); System.out.println(person); } @Test public void test2(){ 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(); }
15 -5 获取运行时类的完整结构
1.可获取的结构
Field、Method、Constructor、Superclass、Interface、Annotation
2.获取的方法
提供类的结构
public class Creature<T> implements Serializable { private char gender; public double weight; private void breath(){ System.out.println("生物呼吸"); } public void eat(){ System.out.println("生物吃东西"); } }
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE}) @Retention(RetentionPolicy.RUNTIME) public @interface MyAnnotation { String value() default "hello"; }
public interface MyInterface { void info(); }
package com.atguigu.java1; /** * @author shkstart * @create 2019 下午 3:12 */ @MyAnnotation(value="hi") public class Person extends Creature<String> implements Comparable<String>,MyInterface{ private String name; int age; public int id; public Person(){} @MyAnnotation(value="abc") private Person(String name){ this.name = name; } Person(String name,int age){ this.name = name; this.age = age; } @MyAnnotation private String show(String nation){ System.out.println("我的国籍是:" + nation); return nation; } public String display(String interests,int age) throws NullPointerException,ClassCastException{ return interests + age; } @Override public void info() { System.out.println("我是一个人"); } @Override public int compareTo(String o) { return 0; } private static void showDesc(){ System.out.println("我是一个可爱的人"); } @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + ", id=" + id + '}'; } }
测试:
public class FieldTest { @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(){ Class clazz = Person.class; Field[] declaredFields = clazz.getDeclaredFields(); for(Field f : declaredFields){ //1.权限修饰符 int modifier = f.getModifiers(); System.out.print(Modifier.toString(modifier) + "\t"); //2.数据类型 Class type = f.getType(); System.out.print(type.getName() + "\t"); //3.变量名 String fName = f.getName(); System.out.print(fName); System.out.println(); } } }
package com.atguigu.java2; import com.atguigu.java1.Person; import org.junit.Test; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.lang.reflect.Modifier; /** * 获取运行时类的方法结构 * * @author shkstart * @create 2019 下午 3:37 */ 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(){ Class clazz = Person.class; 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(); } } }
package com.atguigu.java2; import com.atguigu.java1.Person; import org.junit.Test; import java.lang.annotation.Annotation; import java.lang.reflect.Constructor; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; /** * @author shkstart * @create 2019 下午 4:19 */ 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 superclass = clazz.getSuperclass(); System.out.println(superclass); } /* 获取运行时类的带泛型的父类 */ @Test public void test3(){ Class clazz = Person.class; Type genericSuperclass = clazz.getGenericSuperclass(); System.out.println(genericSuperclass); } /* 获取运行时类的带泛型的父类的泛型 代码:逻辑性代码 vs 功能性代码 */ @Test public void test4(){ Class clazz = Person.class; Type genericSuperclass = clazz.getGenericSuperclass(); ParameterizedType paramType = (ParameterizedType) genericSuperclass; //获取泛型类型 Type[] actualTypeArguments = paramType.getActualTypeArguments(); // System.out.println(actualTypeArguments[0].getTypeName()); System.out.println(((Class)actualTypeArguments[0]).getName()); } /* 获取运行时类实现的接口 */ @Test public void test5(){ Class clazz = Person.class; Class[] interfaces = clazz.getInterfaces(); for(Class c : interfaces){ System.out.println(c); } System.out.println(); //获取运行时类的父类实现的接口 Class[] interfaces1 = clazz.getSuperclass().getInterfaces(); for(Class c : interfaces1){ System.out.println(c); } } /* 获取运行时类所在的包 */ @Test public void test6(){ Class clazz = Person.class; Package pack = clazz.getPackage(); System.out.println(pack); } /* 获取运行时类声明的注解 */ @Test public void test7(){ Class clazz = Person.class; Annotation[] annotations = clazz.getAnnotations(); for(Annotation annos : annotations){ System.out.println(annos); } } }
小 结:
1.在实际的操作中,取得类的信息的操作代码,并不会经常开发。
2.一定要熟悉java.lang.reflect包的作用,反射机制。
3.如何取得属性、方法、构造器的名称,修饰符等。
15-6 调用运行时类的指定结构
一、调用运行时类中指定属性
/* * 如何操作运行时类中指定的是属性 * */ @Test public void testField1()throws Exception{ Class clazz = Person.class; //创建运行时类的对象 Person person = (Person)clazz.newInstance(); //1.getDeclaredField(String fieldName):获取运行时类中指定变量名的属性 Field name = clazz.getDeclaredField("name"); //2.保证当前属性时可访问的 name.setAccessible(true); //3.获取、设置指定对象的此属性值 name.set(person,"Mbappe"); System.out.println(name.get(person)); }
二、调用运行时类中指定方法
/* * 如何操作运行时类中的指定的方法 * */ @Test public void testMethod() throws Exception{ Class clazz = Person.class; //创建运行时对象 Person person = (Person) clazz.newInstance(); /* 1.获取指定的某个方法 getDeclaredMethod():参数1:指明获取的方法的名称 参数2:指明获取的方法的形参列表 */ Method show = clazz.getDeclaredMethod("show", String.class); //2.保证当前的方法时可访问的 show.setAccessible(true); /* 3.调用方法的invoke():参数1:方法的调用者 参数2:给方法形参赋值的实参 invoke()的返回值即为对应类中调用的方法的返回值。 */ Object china = show.invoke(person, "CHINA"); System.out.println(china); System.out.println("****************"); //private static void showDesc() Method showDesc = clazz.getDeclaredMethod("showDesc"); showDesc.setAccessible(true); //如果调用的运行时类中的方法没有返回值,则此invoke()返回null Object invoke = showDesc.invoke(Person.class); System.out.println(invoke); }
三、调用运行时类中指定构造器
@Test public void testConstructor()throws Exception{ Class clazz = Person.class; //创建运行时对象 Person person = (Person) clazz.newInstance(); //private Person(String name): //1.获取指定的构造器 //getDeclaredConstructor():参数:指明构造器的参数列表 Constructor constructor = clazz.getDeclaredConstructor(String.class); //2.保证此构造器是可访问的 constructor.setAccessible(true); //3.调用此构造器创建此运行时类的对象 Person person1 = (Person) constructor.newInstance("Tom"); System.out.println(person1.toString()); }
15 - 7 反射的应用:动态代理
一.代理设计模式的原理:
使用一个代理将对象包装起来, 然后用该代理对象取代原始对象。任何对原 始对象的调用都要通过代理。代理对象决定是否以及何时将方法调用转到原 始对象上。
二、实现
1.静态代理回顾
package com.atguigu.java; /** * @author 张丁野 * @version v1.0 * @time 2022-07-10-19:13 * @Description * 静态代理举例 * * 特点:代理类和被代理类在编译期间,就确定下来了 */ interface ClothFactory{ public abstract void produceCloth(); } //代理类 class ProxyClothFactory implements ClothFactory{ private ClothFactory factory;//用被代理类对象进行实例化 public ProxyClothFactory(ClothFactory factory){ this.factory = factory; } @Override public void produceCloth() { System.out.println("代理工厂做一些准备工作"); factory.produceCloth(); System.out.println("代理工厂做一些后续的收尾工作"); } } //被代理类 class NikeClothFactory implements ClothFactory{ @Override public void produceCloth() { System.out.println("Nike工厂做一些准备工作"); } } public class StaticProxyTest { public static void main(String[] args) { //创建被代理类的对象 NikeClothFactory nike = new NikeClothFactory(); //创建代理类的对象 ProxyClothFactory proxyClothFactory = new ProxyClothFactory(nike); proxyClothFactory.produceCloth(); } }
2.动态代理
要想实现动态代理,需要解决的问题?
1.如何根据加载到内存中的被代理类,动态的创建一个代理类及其对象?
2.当通过代理类的对象调用方法时,如何动态的去调用被代理类中的同名方法?
package com.atguigu.java; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** * @author 张丁野 * @version v1.0 * @time 2022-07-10-19:35 * @Description * * 动态代理的举例: */ interface Human{ public abstract String getBelief(); public abstract 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("我喜欢吃" + food); } } class ProxyFactory{ //调用此方法:返回一个代理类的对象。解决问题一: public static Object getProxyInstance(Object obj){//obj:被代理类的对象 MyInvocationHandler handler = new MyInvocationHandler(); handler.bind(obj); return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(),handler); } } class MyInvocationHandler 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; } } 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("火锅"); } }
优点:
1.没有显式的代理类,可以实现通过改变参数达到实现不同的接口,代理不同的被代理类的效果
2.通过实现InvocationHandler接口,实现代理类invoke()指向被代理类invoke(),从而达到减少编写代理类的代理方法的效果