Java反射开窍--1
1.通过案例引出反射并体会反射的好处
案例:美团外卖 --->付款 ---》要么用微信支付 要么用支付宝支付
package com.zhaoss.test01;
//接口的制定方:美团外卖
public interface Mtwm {
//在线支付功能:
void payOnline();
}
public class WeChat implements Mtwm {
@Override
public void payOnline() {
//具体实现微信支付的功能:
System.out.println("我已经点了外卖,正在使用微信支付");
}
}
public class AliPay implements Mtwm {
@Override
public void payOnline() {
//具体实现微信支付的功能:
System.out.println("我已经点了外卖,正在使用支付宝进行支付");
}
}
public class BankCard implements Mtwm {
@Override
public void payOnline() {
//具体实现微信支付的功能:
System.out.println("我已经点了外卖,正在使用招商银行信用卡支付");
}
}
测试类:
public class Test {
public static void main(String[] args) {
//定义一个字符串,用来模拟前台的支付方式:
String str = "微信";
//str.equals("微信")---避免空指针异常,编程习惯要好,常量在左边
if ("微信".equals(str)) {
// 微信支付:
pay(new WeChat());
}
if ("支付宝".equals(str)) {
//支付宝支付:
pay(new AliPay());
}
if ("招商银行".equals(str)) {
pay(new BankCard());
}
}
//微信支付
public static void pay(WeChat wc) {
wc.payOnline();
}
//支付宝支付
public static void pay(AliPay ap) {
ap.payOnline();
}
//招商银行支付
public static void pay(BankCard bc) {
bc.payOnline();
}
}
看了上面的代码,大家肯定会觉得冗余哈,大家都学过面向对象的多态特性,可以将测试类中的方法进行简化,pay方法定义形参为Mtwm(美团外卖)这个接口,等我们调用时传入对应的实现类即可。于是,代码可以简化为如下:
public class Test {
public static void main(String[] args) {
//定义一个字符串,用来模拟前台的支付方式:
String str = "微信";
if ("微信".equals(str)) {
// 微信支付:
pay(new WeChat());
}
if ("支付宝".equals(str)) {
//支付宝支付:
pay(new AliPay());
}
if ("招商银行".equals(str)) {
pay(new BankCard());
}
}
//支付
public static void pay(Mtwm m) {
m.payOnline();
}
}
多态确实可以提高代码的扩展性,但是:扩展性没有达到最好。
怎么没有达到最好呐:上面的分支判断,还是需要手动的删除或者添加,当接入中国银行/广州银行/花旗银行等的时候,是需要修改这个测试代码类的。
那更好的解决办法:反射机制 利用反射实现上述功能:
public class Test {
public static void main(String[] args) {
//定义一个字符串,用来模拟前台的支付方式:
String str = "com.zhaoss.test01.AliPay"; //字符串:实际上:就是微信类的全限定路径
//下面的代码就是利用反射:
Class cls = Class.forName(str);
/*
代码解读,首先根据str所代表的AliPay的全限定类名,去得到AliPay的字节码对象cls(万物皆对象,这里理解为对象更好懂,接下来还会讲解Class这个类)
根据cls字节码对象去获取AliPay这个类的对象实例o,并根据cls获取字节码对象中的方法并执行
*/
Object o = cls.newInstance();
Method method = cls.getMethod("payOnline");
method.invoke(o);
}
//支付
public static void pay(Mtwm m) {
m.payOnline();
}
这样修改之后,程序变得更加的简洁,并且以后外部又有谁要实现这个接口,这个程序都不必再进行改动,实现了解耦。
2.通过概念再体会反射
反射概念:
JAVA反射机制是在运行状态
中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象, 都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。
帮助理解:
在编译后产生字节码文件的时候,类加载器子系统通过二进制字节流,负责从文件系统加载class文件。 在执行程序(java.exe)时候,将字节码文件读入JVM中--->这个过程叫做类的加载。然后在内存中对应创建一个java.lang.Class对象-->这个对象会被放入字节码信息中,这个Class对象,就对应加载那个字节码信息,这个对象将被作为程序访问方法区中的这个类的各种数据的外部接口。 所以:我们可以通过这个对象看到类的结构,这个对象就好像是一面镜子,透过镜子看到类的各种信息,我们形象的称之为反射 这种“看透”class的能力(the ability of the program to examine itself)被称为introspection(内省、内观、反省)。Reflection和introspection是常被并提的两个术语。
疑惑点,需要解决:每个类被加载的时候,都只加载一次,创建该类对应的Class对象,这个对象会被放进字节码信息中,即每个类都有对应的字节码信息,而每个字节码信息中又存放了该类的Class对象,这个对象是反射得以实现的基础和关键。通过这个对象,获取到该类字节码中的各种信息,构造方法/属性/方法等。其它类加载时都会有一个Class对象创建,那java.lang.Class这个类本身加载的时候,是不是也创建一个自己的Class对象嘞?主要就是加粗这点有疑问,前面说的帮助理解。
补充:
动态语言vs静态语言
1、动态语言
是一类在运行时可以改变其结构的语言:例如新的函数、对象、甚至代码可以 被引进,已有的函数可以被删除或是其他结构上的变化。通俗点说就是在运 行时代码可以根据某些条件改变自身结构。
主要动态语言: Object-C、 C#、JavaScript、 PHP、 Python、 Erlang 。
2、静态语言
与动态语言相对应的,运行时结构不可变的语言就是静态语言。如Java、C、 C++。
所以Java不是动态语言,但Java可以称之为“准动态语言”。即Java有一定的动 态性,我们可以利用反射机制、字节码操作获得类似动态语言的特性。 Java的动态性让编程的时候更加灵活!
3.Class类的理解--万物皆对象
通过Class类,可以得到具体的Class类的对象,字节码信息对象。得到这个后就可以获取剩下的属性方法构造方法等,既而创建该类的对象。
4.获取字节码信息的4种形式
public class Test {
public static void main(String[] args) throws ClassNotFoundException {
//案例:以Person的字节码信息为案例 /
//方式1:通过getClass() 方法获取
Person p = new Person();
Class c1 = p.getClass();
System.out.println(c1);
//方式2:通过内置class属性:
Class c2 = Person.class;
System.out.println(c2);
System.out.println(c1 == c2);
//注意:方式1和方式2 不常用 /
//方式3:--》用的最多:调用Class类提供的静态方法forName
Class c3 = Class.forName("com.zhaoss.test02.Person");
//方式4:利用类的加载器(了解技能点)
ClassLoader loader = Test.class.getClassLoader();
Class c4 = loader.loadClass("com.zhaoss.test02.Person");
}
}
5.可以作为Class这个类的类实例的种类
Class类的具体的实例:
(1)类:外部类,内部类
(2)接口
(3)注解
(4)数组
(5)基本数据类型
(6)void
/**
* @author wangcc
* @create 2020-11-04 1:30
**/
public class Demo {
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 = {5, 6, 7};
Class c5 = arr2.getClass();
System.out.println(c4 == c5);//结果:true .同一个维度,同一个元素类型,得到的字节码就是同一个
Class c6 = int.class;
Class c7 = void.class;
}
}
学习了这篇文章基本上就差不多了,接下来一篇就是反射的api调用,根据反射获取属性,方法,构造方法,各种修饰符的,创建对象,为属性赋值等操作。明天再写,太晚了今天。