案例:美团外卖 --> 付款 --> 要么微信支付,要么支付宝支付
#那么这里由美团制定规则
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
在编译后产生字节码文件的时候,类加载器子系统通过二进制字节流,负责从文件系统加载class文件。在执行程序(java.exe)时候,将字节码文件读入jvm中-->这个过程叫做类的加载。然后在内存中对应创建一个java.lang.Class对象-->这个对象会被放入字节码信息中,这个class对象对应加载那个字节码3信息,这个对象被做为程序访问方法区中的这个类的各和数据的外部接口。
所以:我们可以通过这个对象看到类的结构,这个对象是一面镜子,透过镜子看到类的各种信息,我们形象的称之为反射,这种”看透”class能力被称为introspection(内省,内现,反省)。Reflection和instrospection是常被提的两个术语。
说明:在运行期间,如果我们要产生某个类的对象,java虚拟机(jvm)会检查该类型的class对象是否已被加载。
如果没有加载,jvm会根据类的名称找到.class文件并加载它。一旦某个类型的class对象已经被加载到内存中,就可以用它来产生该类型的所有对象。
面向对象思维:万物皆对象, 将“类”当成一个对象,Class将这个“类”的抽象成对象,
想要获取到属性:class类--> 得到具体的实例(对象) --> 通过对象得到属性
提前准备下面这几个类、接口,注解
public class AliPay implements Mtwm { @Override public void payonline() { System.out.println("我已经点了外卖,正在使用支付宝支付"); } }
public class Wechat implements Mtwm { @Override public void payonline() { // 具体实现微信支付的功能 System.out.println("我已经点了外卖,正在使用微信支付"); } }
public interface Mtwm { void payonline(); }
// 处定义的接口 public interface MyInterface { void myMethod(); }
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE}) @Retention(RetentionPolicy.RUNTIME) public @interface MyAnnotation { String value(); // 属性 }
public class Person implements Serializable { private int age; public String name; private void eat(){ System.out.println("Person --- eat"); } public void sleep(){ System.out.println("Person --- sleep"); } }
@MyAnnotation(value = "hello") public class Student extends Person implements MyInterface { // 属性 private int sno; double height; protected double weight; public double score; // 方法 @MyAnnotation(value = "method") public String showInfo(){ return "我是一名三好学生"; } public String showInfo(int a, int b){ return "重载---我是一名三好学生"; } private void work(){ System.out.println("我以后会找工作 --> 成码农"); } private void work(int a){ System.out.println("重载 === 我以后会找工作 --> 成码农"); } // 构造器 private Student(int sno) { this.sno = sno; } public Student() { } Student(int sno, double weight) { this.sno = sno; this.weight = weight; } protected Student(int sno, double weight, double hight){ this.sno = sno; this.weight = weight; this.height = hight; } void happy(){ System.out.println("做人最重要的就是开心"); } @Override @MyAnnotation(value = "helloMyMethod") public void myMethod() throws RuntimeException{ System.out.println("我重写了myMethod方法"); } protected int getSno(){ return sno; } @Override public String toString() { return "Student{" + "sno=" + sno + ", height=" + height + ", weight=" + weight + ", score=" + score + '}'; } }
引入:我们在点外卖的时候选择支付方式,可以是微信,支付宝,或者是美团支付,如果是常规的写法,通过判断来确定是new那一个对象。如果说后期我需要引入其它的支付方式来进行支付,那我们又要加入其它的判断来扩展其它的支付方式。那这里程序的扩展性就不好
public class TestReflect { public static void main(String[] args) { // 定义一个字符串用来模拟前台的支付方式 int i = 1; if (i == 1) { // new Wechat().payonline(); pay(new Wechat()); } else { // new AliPay().payonline(); pay(new AliPay()); } } // public static void pay(Wechat wc){ // wc.payonline(); // } // public static void pay(AliPay ap) { // ap.payonline(); // } // 方法的形参是接口,具体传入的是接口中的实现类的对象 --> 多态的一种体现 public static void pay(Mtwm m){ m.payonline(); } } // 多态确实可以提高代码的扩展性,但是:扩展性没有达到最好. // 怎么没有达到最好:上面的分支,还是需要手动的删除或者添加 // 解决办法: 反射机制
上面的代码我们通过反射来做,可以明显的看出来我们只需通过修改以对应class的路径就可以new出不同的对象,这样我们的代码就不用修改,后期扩展比较方便
public class TestReflect2 { public static void main(String[] args) throws Exception{ // 字符串,实际上:就是微信类的全限定路径 String str = "com.mashibing.sync.entity.Wechat"; //下面的代码 就是利用反射 Class> cls = Class.forName(str); Object o = cls.newInstance(); Method method = cls.getMethod("payOnline"); method.invoke(o); } }
--------------------------------------------------------------------------------------------
4种获取字节码的方式
public class TestReflect3 { // 案例: 以person的字节码信息为案例 public static void main(String[] args) throws Exception{ Person p = new Person(); Class c1 = p.getClass(); System.out.println(c1); // class com.mashibing.sync.entity.Person Class c2 = Person.class; System.out.println(c2); // 用的最多的 Class> c3 = Class.forName("com.mashibing.sync.entity.Person"); System.out.println(c3); // 利用类加载器(了解) ClassLoader classLoader = TestReflect3.class.getClassLoader(); Class c4 = classLoader.loadClass("com.mashibing.sync.entity.Person"); System.out.println(c4); } }
Class类的具体的实例
- 类,外部类,内部类
- 接口
- 注解
- 数组
- 基本数据类型
- Void
public class TestReflect4 { public static void main(String[] args) { Class c1 = Person.class; Class c2 = Comparable.class; Class c3 = Override.class; int[] arr1 = {1, 2, 3}; Class c4 = arr1.getClass(); int[] arr2 = {2, 3, 4}; Class c5 = arr2.getClass(); // 同一个维度的数组,同一个元素类型,得到的字节码就是同一个 System.out.println(c4 == c5); Class c6 = int.class; Class c7 = void.class; } }
获取对象的构造器
public class TestReflect5 { public static void main(String[] args) throws Exception { // 获取字节码信息 Class cls = Student.class; // 通过字节码信息获取构造器 // 只能获取当前运行时类的被public修饰的构造器 Constructor[] constructors = cls.getConstructors(); for (Constructor c: constructors) { System.out.println(c); } System.out.println("----------------------------------------"); // 获取到所有的构造器 Constructor[] declaredConstructors = cls.getDeclaredConstructors(); for (Constructor d: declaredConstructors) { System.out.println(d); } System.out.println("------------------------------------------"); // 什么都不传入,获取到的是空参构造器, 只能获取到 public修饰的 Constructor c1 = cls.getConstructor(); System.out.println(c1); System.out.println("-----------------------------------------"); // 获取到两个参数的构造器, 可以获取到private, protected修饰的 Constructor c2 = cls.getDeclaredConstructor(int.class, double.class); System.out.println(c2); // 有了构造器以后,我们就可以创建对象了 Object o = c1.newInstance(); System.out.println(o); // 调用非空参构造 Object o1 = c2.newInstance(10, 12D); System.out.println(o1); } }
获取对象属性,及属性结构
public class TestReflect6 { public static void main(String[] args) throws Exception { Class cls = Student.class; // 获取到属性, 可以获取到当前类与父类中被public修饰的属性 Field[] fields = cls.getFields(); for (Field f: fields){ System.out.println(f); } System.out.println("--------------------------"); // 获取到所有的属性, 当前类中所有的属性,不包括父类中的所有属性 Field[] f1 = cls.getDeclaredFields(); for (Field f: f1) { System.out.println(f); } System.out.println("-----------------------------"); // 获取到指定的属性, 只能获取到public修饰的 Field score = cls.getField("score"); System.out.println(score); System.out.println("-----------------------------"); // 获取到被private修饰的属性 Field sno = cls.getDeclaredField("sno"); System.out.println(sno); System.out.println("-----------------------------"); // 属性的具体结构 // 获取修饰符 public static final String int modifiers = sno.getModifiers(); System.out.println(Modifier.toString(modifiers)); // 获取属性的数据类型 Class clazz = sno.getType(); System.out.println(clazz.getName()); // 获取属性的名字 String name = sno.getName(); System.out.println(name); System.out.println("------------------------------"); Field sco = cls.getField("score"); Object obj = cls.newInstance(); // 给属性赋值必须有对象 sco.set(obj, 98); System.out.println(obj); } }
获取对象所在的包,注解、接口
public class TestReflect7 { public static void main(String[] args) { Class cls = Student.class; // 获取运行时类的接口 Class[] interfaces = cls.getInterfaces(); for (Class c : interfaces) { System.out.println(c); } System.out.println("-----------------------------"); // 得到父类的接口 // 获取到父类的字节码信息 Class superclass = cls.getSuperclass(); // 得到接口 Class[] i1 = superclass.getInterfaces(); for (Class i : i1) { System.out.println(i); } System.out.println("-----------------------------"); // 获取到运行时类所在的包 Package aPackage = cls.getPackage(); System.out.println(aPackage); System.out.println(aPackage.getName()); System.out.println("-----------------------------"); // 获取到运行时类的注解, 必须是运行时注解 声明:@Retention(RetentionPolicy.RUNTIME) Annotation[] annotations = cls.getAnnotations(); for (Annotation a: annotations) { System.out.println(a); } } }
获取对象的异常、方法以调用方法
public class TestReflect8 { public static void main(String[] args) throws Exception { Class cls = Student.class; // 获取于是所有的public的方法, 会包含其它所有父类中的方法 Method[] methods = cls.getMethods(); for (Method m: methods) { System.out.println(m); } System.out.println("-----------------------------"); // 获取到 当前运行时类中的所有的方法 Method[] declaredMethods = cls.getDeclaredMethods(); for (Method d : declaredMethods) { System.out.println(d); } System.out.println("-----------------------------"); // 获取到当前运行时类指定的方法 Method showInfo = cls.getMethod("showInfo", int.class, int.class); System.out.println(showInfo); System.out.println("-----------------------------"); // 获取到private修饰的方法 Method work = cls.getDeclaredMethod("work", int.class); System.out.println(work); // 获取方法的具体的结构 // 修饰符: 返回值类型 方法名(参数列表) throw XXXX{} System.out.println(work.getName()); int modifiers = work.getModifiers(); System.out.println(Modifier.toString(modifiers)); System.out.println(work.getReturnType()); Class[] parameterTypes = work.getParameterTypes(); for (Class par: parameterTypes) { System.out.println(par); } System.out.println("-----------------------------"); // 获取注解, 只能获取到运行时的注解 Method myMethod = cls.getMethod("myMethod"); Annotation[] annotations = myMethod.getAnnotations(); for (Annotation a : annotations) { System.out.println(a); } System.out.println("-----------------------------"); // 获取异常 Class [] exceptionTypes = myMethod.getExceptionTypes(); for (Class e : exceptionTypes) { System.out.println(e); } System.out.println("-----------------------------"); // 调用方法 Object o = cls.newInstance(); // 调用o对象的myMethod方法 myMethod.invoke(o); // 调用带参的方法 System.out.println(showInfo.invoke(o, 12, 45)); } }
关于反射的面试题:
- 创建person的对象,以后用new Person()创建还是用反射创建
需要体现程序的动态性的时候使用反射来创建对象;不知道是创建那个对象,只有在运行的时候才知道是创建那一个对象,比如说我们在点外卖的时候选择支付方式,可以微信支付、支付宝支付、或者是美团支付只有在用户选择后我们才知道去创建那个对象,这个时候我们就需要修改反射来创建对象。
- 反射是否破坏了面向对象的封装性
破坏了面向对象的封装性,但是二者并不冲突。对象的封装主要是解决安全性,反射的提出主要是为了解决程序的动态性。 对象的privte修饰的属性、方法是就明确的告诉就不要使用这里的属性和方法; 但反射确实是调用到或者使用到private修饰的方法或属性。一般来说是不建议使用这里对象里面的private修饰的属性或者方法。因此这里说他们是不冲突的。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)