java 反射
第十六章 反射
通过案例体会反射的好处
案例:美团外卖 --->付款 ---》要么用微信支付 要么用支付宝支付
1 package com.llh; 2 3 //接口的制定方:美团外卖 4 public interface Mtwm { 5 //在线支付功能: 6 void payOnline(); 7 }
1 public class WeChat implements Mtwm{ 2 @Override 3 public void payOnline() { 4 //具体实现微信支付的功能: 5 System.out.println("我已经点了外卖,正在使用微信支付"); 6 } 7 }
1 public class AliPay implements Mtwm { 2 @Override 3 public void payOnline() { 4 //具体的支付宝支付: 5 System.out.println("我已经点了外卖,我正在使用支付宝进行支付"); 6 } 7 }
1 public class BankCard implements Mtwm{ 2 @Override 3 public void payOnline() { 4 System.out.println("我已经定了外卖,我正在用招商银行信用卡支付"); 5 } 6 }
测试类:
1 package com.llh; 2 3 public class Test { 4 public static void main(String[] args) { 5 //定义一个字符串,用来模拟前台的支付方式: 6 String str = "微信"; 7 if("微信".equals(str)){//str.equals("微信")---?避免空指针异常 8 //微信支付: 9 //new WeChat().payOnline(); 10 pay(new WeChat()); 11 } 12 if("支付宝".equals(str)){ 13 //支付宝支付: 14 //new AliPay().payOnline(); 15 pay(new AliPay()); 16 } 17 if("招商银行".equals(str)){ 18 pay(new BankCard()); 19 } 20 } 21 //微信支付 22 public static void pay(WeChat wc){ 23 wc.payOnline(); 24 } 25 //支付宝支付 26 public static void pay(AliPay ap){ 27 ap.payOnline(); 28 } 29 //招商银行支付 30 public static void pay(BankCard bc){ 31 bc.payOnline(); 32 } 33 }
为了提高代码的扩展性---》面向对象特性:多态:
1 package com.llh; 2 3 public class Test { 4 public static void main(String[] args) { 5 //定义一个字符串,用来模拟前台的支付方式: 6 String str = "微信"; 7 if("微信".equals(str)){//str.equals("微信")---?避免空指针异常 8 //微信支付: 9 pay(new WeChat()); 10 } 11 if("支付宝".equals(str)){ 12 //支付宝支付: 13 pay(new AliPay()); 14 } 15 if("招商银行".equals(str)){ 16 pay(new BankCard()); 17 } 18 } 19 //方法形参是接口,具体传入的是接口的实现类的对象---》多态的一种形式 20 public static void pay(Mtwm m){ 21 m.payOnline(); 22 } 23 24 }
多态确实可以提高代码的扩展性,但是:扩展性没有达到最好。
怎么没有达到最好:上面的分支,还是需要手动的删除或者添加。
解决办法:反射机制
利用反射实现上述功能:
1 package com.llh; 2 3 import java.lang.reflect.InvocationTargetException; 4 import java.lang.reflect.Method; 5 6 public class Demo { 7 public static void main(String[] args) throws Exception { 8 //定义一个字符串,用来模拟前台的支付方式: 9 String str = "com.zhaoss.test01.AliPay"; //字符串:实际上:就是微信类的全限定路径 10 //下面的代码就是利用反射: 11 Class cls = Class.forName(str);//cls-->Class类具体的对象--》AliPay字节码信息 12 Object o = cls.newInstance(); 13 Method method = cls.getMethod("payOnline"); 14 method.invoke(o); 15 } 16 }
通过概念再体会反射
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,
都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。
在编译后产生字节码文件的时候,类加载器子系统通过二进制字节流,负责从文件系统加载class文件。
在执行程序(java.exe)时候,将字节码文件读入JVM中--->这个过程叫做类的加载。然后在内存中对应创建一个java.lang.Class对象-->这个对象会被放入字节码信息中,这个Class对象,就对应加载那个字节码信息,这个对象将被作为程序访问方法区中的这个类的各种数据的外部接口。
所以:我们可以通过这个对象看到类的结构,这个对象就好像是一面镜子,透过镜子看到类的各种信息,我们形象的称之为反射
这种“看透”class的能力(the ability of the program to examine itself)被称为introspection(内省、内观、反省)。Reflection和introspection是常被并提的两个术语。
说明:在运行期间,如果我们要产生某个类的对象,Java虚拟机(JVM)会检查该类型的Class对象是否已被加载。
如果没有被加载,JVM会根据类的名称找到.class文件并加载它。一旦某个类型的Class对象已被加载到内存,就可以用它来产生该类型的所有对象。
补充:
动态语膏vs静态语言
1、动态语言
是一类在运行时可以改变其结构的语言:例如新的函数、对象、甚至代码可以
被引进,已有的函数可以被删除或是其他结构上的变化。通俗点说就是在运
行时代码可以根据某些条件改变自身结构。
主要动态语言: Object-C、 C#、JavaScript、 PHP、 Python、 Erlang 。
2、静态语言
与动态语言相对应的,运行时结构不可变的语言就是静态语言。如Java、C、
C++。
所以Java不是动态语言,但Java可以称之为“准动态语言”。即Java有一定的动
态性,我们可以利用反射机制、字节码操作获得类似动态语言的特性。
Java的动态性让编程的时候更加灵活!
Class类的理解
提供丰富的类
1 package com.llh; 2 3 //作为一个父类 4 public class Person { 5 //属性 6 private int age; 7 public String name; 8 //方法 9 private void eat(){ 10 System.out.println("Person---eat"); 11 } 12 public void sleep(){ 13 System.out.println("Person---sleep"); 14 } 15 }
1 package com.llh; 2 3 import com.llh1.Person; 4 5 //Student作为子类 6 public class Student extends Person { 7 //属性: 8 private int sno;//学号 9 double height;//身高 10 protected double weight;//体重 11 public double score;//成绩 12 //方法: 13 public String showInfo(){ 14 return "我是一名三好学生"; 15 } 16 private void work(){ 17 System.out.println("我以后会找工作--》成为码农 程序员 程序猿 程序媛"); 18 } 19 //构造器 20 public Student(){ 21 System.out.println("空参构造器"); 22 } 23 private Student(int sno){ 24 this.sno = sno; 25 } 26 Student(int sno,double weight){ 27 this.sno = sno; 28 this.weight = weight; 29 } 30 }
获取字节码信息的四种形式
1 package com.llh; 2 3 import com.llh1.Person; 4 5 public class Test { 6 public static void main(String[] args) throws ClassNotFoundException { 7 //案例:以Person的字节码信息为案例 8 //方式1:通过getClass()方法获取 9 Person p = new Person(); 10 Class c1 = p.getClass(); 11 System.out.println(c1); 12 //方式2:通过内置class属性: 13 Class c2 = Person.class; 14 System.out.println(c2); 15 System.out.println(c1==c2); 16 //注意:方式1和方式2 不常用 17 //方式3:--》用的最多:调用Class类提供的静态方法forName 18 Class c3 = Class.forName("com.zhaoss.test02.Person"); 19 //方式4:利用类的加载器(了解技能点) 20 ClassLoader loader = Test.class.getClassLoader(); 21 Class c4 = loader.loadClass("com.zhaoss.test02.Person"); 22 } 23 }
可以作为Class类的实例的种类
Class类的具体的实例:
(1)类:外部类,内部类
(2)接口
(3)注解
(4)数组
(5)基本数据类型
(6)void
验证:
1 package com.llh; 2 3 import com.llh1.Person; 4 5 public class Demo { 6 public static void main(String[] args) { 7 /* 8 Class类的具体的实例: 9 (1)类:外部类,内部类 10 (2)接口 11 (3)注解 12 (4)数组 13 (5)基本数据类型 14 (6)void 15 */ 16 Class c1 = Person.class; 17 Class c2 = Comparable.class; 18 Class c3 = Override.class; 19 int[] arr1 = {1,2,3}; 20 Class c4 = arr1.getClass(); 21 int[] arr2 = {5,6,7}; 22 Class c5 = arr2.getClass(); 23 System.out.println(c4==c5);//结果:true .同一个维度,同一个元素类型,得到的字节码就是同一个 24 25 Class c6 = int.class; 26 Class c7 = void.class; 27 } 28 }
获取运行时类的完整结构
补充完善上面提供的丰富的类
1 //作为一个父类 2 public class Person implements Serializable { 3 //属性 4 private int age; 5 public String name; 6 //方法 7 private void eat(){ 8 System.out.println("Person---eat"); 9 } 10 public void sleep(){ 11 System.out.println("Person---sleep"); 12 } 13 }
1 //Student作为子类 2 @MyAnnotation(value="hello") 3 public class Student extends Person implements MyInterface{ 4 //属性: 5 private int sno;//学号 6 double height;//身高 7 protected double weight;//体重 8 public double score;//成绩 9 //方法: 10 @MyAnnotation(value="himethod") 11 public String showInfo(){ 12 return "我是一名三好学生"; 13 } 14 public String showInfo(int a,int b){ 15 return "重载方法====我是一名三好学生"; 16 } 17 private void work(){ 18 System.out.println("我以后会找工作--》成为码农 程序员 程序猿 程序媛"); 19 } 20 void happy(){ 21 System.out.println("做人最重要的就是开心每一天"); 22 } 23 protected int getSno(){ 24 return sno; 25 } 26 //构造器 27 public Student(){ 28 System.out.println("空参构造器"); 29 } 30 private Student(int sno){ 31 this.sno = sno; 32 } 33 Student(int sno,double weight){ 34 this.sno = sno; 35 this.weight = weight; 36 } 37 protected Student(int sno,double height,double weight){ 38 this.sno = sno; 39 } 40 @Override 41 @MyAnnotation(value="hellomyMethod") 42 public void myMethod() { 43 System.out.println("我重写了myMethod方法。。"); 44 } 45 @Override 46 public String toString() { 47 return "Student{" + 48 "sno=" + sno + 49 ", height=" + height + 50 ", weight=" + weight + 51 ", score=" + score + 52 '}'; 53 } 54 }
1 import java.lang.annotation.Retention; 2 import java.lang.annotation.RetentionPolicy; 3 import java.lang.annotation.Target; 4 import static java.lang.annotation.ElementType.*; 5 import static java.lang.annotation.ElementType.LOCAL_VARIABLE; 6 /* 7 @Target:定义当前注解能够修饰程序中的哪些元素 8 @Retention:定义注解的声明周期 9 */ 10 @Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE}) 11 @Retention(RetentionPolicy.RUNTIME) 12 public @interface MyAnnotation { 13 String value();//属性 14 }
1 public interface MyInterface {//自定义的接口 2 //随便定义一个抽象方法: 3 void myMethod(); 4 }
获取构造器和创建对象
1 import java.lang.reflect.Constructor; 2 import java.lang.reflect.InvocationTargetException; 3 public class Test01 { 4 public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { 5 //获取字节码信息: 6 Class cls = Student.class; 7 //通过字节码信息可以获取构造器: 8 //getConstructors只能获取当前运行时类的被public修饰的构造器 9 Constructor[] c1 = cls.getConstructors(); 10 for(Constructor c:c1){ 11 System.out.println(c); 12 } 13 System.out.println("-------------------"); 14 //getDeclaredConstructors:获取运行时类的全部修饰符的构造器 15 Constructor[] c2 = cls.getDeclaredConstructors(); 16 for(Constructor c:c2){ 17 System.out.println(c); 18 } 19 System.out.println("-------------------"); 20 //获取指定的构造器: 21 //得到空构造器 22 Constructor con1 = cls.getConstructor(); 23 System.out.println(con1); 24 //得到两个参数的有参构造器: 25 Constructor con2 = cls.getConstructor(double.class, double.class); 26 System.out.println(con2); 27 //得到一个参数的有参构造器:并且是private修饰的 28 Constructor con3 = cls.getDeclaredConstructor(int.class); 29 System.out.println(con3); 30 //有了构造器以后我就可以创建对象: 31 Object o1 = con1.newInstance(); 32 System.out.println(o1); 33 Object o2 = con2.newInstance(180.5, 170.6); 34 System.out.println(o2); 35 } 36 }
获取属性和对属性进行赋值
1 import java.lang.reflect.Field; 2 import java.lang.reflect.Modifier; 3 public class Test02 { 4 public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, InstantiationException { 5 //获取运行时类的字节码信息: 6 Class cls = Student.class; 7 //获取属性: 8 //getFields:获取运行时类和父类中被public修饰的属性 9 Field[] fields = cls.getFields(); 10 for(Field f:fields){ 11 System.out.println(f); 12 } 13 System.out.println("---------------------"); 14 //getDeclaredFields:获取运行时类中的所有属性 15 Field[] declaredFields = cls.getDeclaredFields(); 16 for(Field f:declaredFields){ 17 System.out.println(f); 18 } 19 System.out.println("---------------------"); 20 //获取指定的属性: 21 Field score = cls.getField("score"); 22 System.out.println(score); 23 Field sno = cls.getDeclaredField("sno"); 24 System.out.println(sno); 25 System.out.println("---------------------"); 26 //属性的具体结构: 27 //获取修饰符 28 /*int modifiers = sno.getModifiers(); 29 System.out.println(modifiers); 30 System.out.println(Modifier.toString(modifiers));*/ 31 System.out.println(Modifier.toString(sno.getModifiers())); 32 //获取属性的数据类型: 33 Class clazz = sno.getType(); 34 System.out.println(clazz.getName()); 35 //获取属性的名字: 36 String name = sno.getName(); 37 System.out.println(name); 38 System.out.println("-------------------------------"); 39 //给属性赋值:(给属性设置值,必须要有对象) 40 Field sco = cls.getField("score"); 41 Object obj = cls.newInstance(); 42 sco.set(obj,98);//给obj这个对象的score属性设置具体的值,这个值为98 43 System.out.println(obj); 44 } 45 }
获取方法和调用方法
1 import java.lang.annotation.Annotation; 2 import java.lang.reflect.InvocationTargetException; 3 import java.lang.reflect.Method; 4 import java.lang.reflect.Modifier; 5 public class Test03 { 6 public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException { 7 //获取字节码信息: 8 Class cls = Student.class; 9 //获取方法: 10 //getMethods:获取运行时类的方法还有所有父类中的方法(被public修饰) 11 Method[] methods = cls.getMethods(); 12 for(Method m:methods){ 13 System.out.println(m); 14 } 15 System.out.println("-----------------------"); 16 //getDeclaredMethods:获取运行时类中的所有方法: 17 Method[] declaredMethods = cls.getDeclaredMethods(); 18 for(Method m:declaredMethods){ 19 System.out.println(m); 20 } 21 System.out.println("-----------------------"); 22 //获取指定的方法: 23 Method showInfo1 = cls.getMethod("showInfo"); 24 System.out.println(showInfo1); 25 Method showInfo2 = cls.getMethod("showInfo", int.class, int.class); 26 System.out.println(showInfo2); 27 Method work = cls.getDeclaredMethod("work",int.class); 28 System.out.println(work); 29 System.out.println("-----------------------"); 30 //获取方法的具体结构: 31 /* 32 @注解 33 修饰符 返回值类型 方法名(参数列表) throws XXXXX{} 34 */ 35 //名字: 36 System.out.println(work.getName()); 37 //修饰符: 38 int modifiers = work.getModifiers(); 39 System.out.println(Modifier.toString(modifiers)); 40 //返回值: 41 System.out.println(work.getReturnType()); 42 //参数列表: 43 Class[] parameterTypes = work.getParameterTypes(); 44 for(Class c:parameterTypes){ 45 System.out.println(c); 46 } 47 //获取注解: 48 Method myMethod = cls.getMethod("myMethod"); 49 Annotation[] annotations = myMethod.getAnnotations(); 50 for(Annotation a:annotations){ 51 System.out.println(a); 52 } 53 //获取异常: 54 Class[] exceptionTypes = myMethod.getExceptionTypes(); 55 for(Class c:exceptionTypes){ 56 System.out.println(c); 57 } 58 //调用方法: 59 Object o = cls.newInstance(); 60 myMethod.invoke(o);//调用o对象的mymethod方法 61 System.out.println(showInfo2.invoke(o,12,45));; 62 } 63 }
获取类的接口,所在包,注解
1 import java.lang.annotation.Annotation; 2 public class Test04 { 3 public static void main(String[] args) { 4 //获取字节码信息: 5 Class cls = Student.class; 6 //获取运行时类的接口: 7 Class[] interfaces = cls.getInterfaces(); 8 for(Class c:interfaces){ 9 System.out.println(c); 10 } 11 //得到父类的接口: 12 //先得到父类的字节码信息: 13 Class superclass = cls.getSuperclass(); 14 //得到接口: 15 Class[] interfaces1 = superclass.getInterfaces(); 16 for(Class c:interfaces1){ 17 System.out.println(c); 18 } 19 //获取运行时类所在的包: 20 Package aPackage = cls.getPackage(); 21 System.out.println(aPackage); 22 System.out.println(aPackage.getName()); 23 //获取运行类的注解: 24 Annotation[] annotations = cls.getAnnotations(); 25 for(Annotation a:annotations){ 26 System.out.println(a); 27 } 28 } 29 }
关于反射的面试题
【1】问题1:创建Person的对象,以后用new Person()创建,还是用反射创建?
虽然说都可以创建对应的实例,但我们说反射之所以提供这种方式是为了在运行期间获取实例的相关属性,所以用什么方式创建其实是取决于我们什么情况下需要用反射。
【2】问题2:反射是否破坏了面向对象的封装性?
确实是的,但是相对于破坏封装性这点来说其实反射的强大作用完全可以忽略这个小缺点,本质上我们封装的属性或方法只是为了不对外提供,但这是建议的,如果想强制使用那确实也能获取到(比如python中可以通过类名.__属性名获取封装属性一样)。举个不恰当的例子:公共厕所分男女,但并不是说男士绝对不可以进女厕所,同样女士绝对不可以进男厕所。这只是个规范性问题。