java反射机制
问题提出
一个公司要开发本公司APP,某程序员在支付功能处给出了两种解决方案-----
第一种---------
利用多态来完成
第二种--------
是利用反射机制完成
代码实现
代码实现----多态
class AlipayM implements WoerMa{
@Override
public void payOnline() {
System.out.println("阿里支付");
}
}
class WechatPayM implements WoerMa {
@Override
public void payOnline() {
System.out.println("微信支付");
}
}
class WoermaCard implements WoerMa {
@Override
public void payOnline() {
System.out.println("沃尔玛钱包");
}
}
public class ZhaoHang implements WoerMa{
@Override
public void payOnline() {
System.out.println("招行支付");
}
}
测试方法-----
public class WoermaTest {
public static void main(String[] args) {
String string= "微信";
switch(string){
case "招行" :
pay(new ZhaoHang());
break;
case "阿里":
pay(new AlipayM());
break;
case "微信":
pay(new WechatPayM());
break;
default:
pay(new WoermaCard());
}
}
public static void pay(WoerMa woerMa){
woerMa.payOnline();
}
}
以上方式虽然用了多态的方式(参数为接口,传入的为参数的实现类)解决了一些问题,但仍然会有维护起来比较繁琐的问题既要维护实现类,又要维护测试类.
代码实现----反射
需要修改测试类即可,
以后再次添加支付方式时只需要维护该实现类即可;
public class WoermaTest {
public static void main(String[] args) {
String string= "com.woerma.client.ZhaoHang";
try {
Class client=Class.forName(string);//通过路径来获得类
Object obj= client.newInstance();
//在最新版本中通过newInstance()方法直接获得实例已被弃用;
//此种方式要求的是类中有无参的构造
Method method=client.getMethod("payOnline");
method.invoke(obj);
} catch (Exception e) {
e.printStackTrace();
}
}
可以通过获得构造方法来间接获得实例;
通过获得构造方法来间接获得实例适用于有参和无参的构造;
public class WoermaTest {
public static void main(String[] args) {
String string= "com.woerma.client.ZhaoHang";
try {
Class client=Class.forName(string);//通过路径来获得类
Constructor constructor = client.getConstructor();
Object obj = constructor.newInstance();
Method method=client.getMethod("payOnline");
method.invoke(obj);
} catch (Exception e) {
e.printStackTrace();
}
}
}
创建对象的方式:
1、使用Class对象的newInstance()方法创建该Class对象的实例,此时该Class对象必须要有无参数的构造方法,在最新的JDK版本中这种方式已被弃用;
2、使用Class对象获取指定的Constructor对象,再调用Constructor的newInstance()方法创建对象类的实例,此时可以选择使用某个构造方法。如果这个构造方法被私有化起来,那么必须先申请访问,将可以访问设置为true;
类中有无参构造-----
class Alibaba{
public Alibaba() {
System.out.println("创建对象成功");
}
}
public class reflecTest {
public static void main(String[] args) throws ClassNotFoundException {
//创建对象
//传统方式
new Alibaba();
//反射方式
Class Ali=Class.forName("com.woerma.client.Alibaba");
Ali.newInstance();
}
}
以上通过无参来直接获得类的实例的方式在新版本种已被废弃;
类中没有无参构造------
如果依旧是上面的代码----会出现错误,
class Alibaba{
String name;
int productNum;
public Alibaba(String name, int productNum) {
this.name = name;
this.productNum = productNum;
System.out.println(name+"------"+productNum);
System.out.println("创建有参对象成功");
}
}
public class reflecTest {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
//创建对象
//传统方式
new Alibaba("圆珠笔",100);
//反射方式
Class Ali=Class.forName("com.woerma.client.Alibaba");
Constructor constructor = Ali.getConstructor(String.class,int.class);//参数类型
/*或者这个方法---- Constructor constructor = Ali.getDeclaredConstructor(String.class,int.class);*/
Object o = constructor.newInstance("圆珠笔",100);
}
}
接下来看构造方法私有化的情况下
class Alibaba{
private String name;
private int productNum;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getProductNum() {
return productNum;
}
public void setProductNum(int productNum) {
this.productNum = productNum;
}
private Alibaba(String name, int productNum) {
this.name = name;
this.productNum = productNum;
System.out.println(name+"------"+productNum);
System.out.println("创建有参对象成功");
}
static Alibaba alibaba= new Alibaba("圆珠笔",100);
public static Alibaba getInstance(){
return alibaba;
}
}
public class reflecTest {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
//创建对象
//传统方式
Alibaba.getInstance();//单例设计
//反射方式
Class Ali=Class.forName("com.woerma.client.Alibaba");
// Constructor constructor = Ali.getConstructor(String.class,int.class);//参数类型
Constructor constructor = Ali.getDeclaredConstructor(String.class,int.class);
constructor.setAccessible(true);//获得访问权限
Object o = constructor.newInstance("圆珠笔",100);
}
}
getDeclaredConstructor()与getConstructor()的区别
从代码中观察----
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
class A{
String name;
public A() {
System.out.println("获得了无参实例");
}
private A(String name){
System.out.println("获得了有参实例");
}
}
public class ReflectTest0001 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Class cla=Class.forName("com.woerma.client.A");
Constructor constructor = cla.getConstructor(String.class);
Constructor declaredConstructor = cla.getDeclaredConstructor(String.class);
constructor.setAccessible(true);
constructor.newInstance("李四");
declaredConstructor.setAccessible(true);
declaredConstructor.newInstance("张三");
}
}
在访问公有构造器时两者都可以,但是当用getConstructor()访问私有构造器时取出现了----错误
所以两者区别如下----
getConstructor()返回指定参数类型public的构造器。
getDeclaredConstructor()返回指定参数类型的private和public构造器。
同时要注意在访问私有构造时要先设定方位权限为true,不然getDeclaredConstructor()也不能访问;
由此来看单例设计也并不安全,
来看枚举类中的反射情况-----
enum ThreeColor{
RED("红"),
GREEN("绿"),
YELLOW("黄");
String desc;
ThreeColor(String desc){
this.desc=desc;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
}
public class ReflectTest0001 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Class cla=Class.forName("com.woerma.client.ThreeColor");
Constructor declaredConstructor = cla.getDeclaredConstructor(String.class);
declaredConstructor.setAccessible(true);
declaredConstructor.newInstance("红");
}
}
对枚举而言,连构造器都获得不了,所以没法获得实例,所以对于实例个数限定的可以用枚举来以确保内存的安全性
同理就可以理解 对应其他的getDeclaredxxx()方法和getxxx()方法的区别
创建对象之前的工作----加载类的字节码信息
四种方式可以加载类的字节码信息-----
class Student{}
public class Person {
public static void main(String[] args) throws ClassNotFoundException {
//方式一----直接通过系统内置类加载器获得
Class aClass = Student.class;
//方式二----通过调用对象的getClass()方法获得
Student per= new Student();
Class aClass1 = per.getClass();
System.out.println(aClass);
//方式三---
Class aClass2=Class.forName("com.gavin.flect.Student");
//方式四-----
ClassLoader classLoader=Person.class.getClassLoader();//获得来的加载器
Class aClass3 = classLoader.loadClass("com.gavin.flect.Student");//通过类加载器来完成字节码信息的加载
System.out.println(aClass==aClass1);
System.out.println(aClass1==aClass2);
System.out.println(aClass2==aClass3);
}
}
最常用的为第三种;
通过反射还能获取啥信息?
简单地说是能获取该类在运行时所有的信息-----属性,方法等;
也能获得父类的一些信息;
获得属性,方法,注解------
通过反射获得属性(包括修饰符,属性名)和方法(包括参数类型,返回值)----
package com.gavin.flect;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
/**
* @author : Gavin
* @date: 2021/8/26 - 08 - 26 - 15:47
* @Description: com.gavin.flect
* @version: 1.0
*/
class FatherTest {
@MySon(name="李二狗他爸",age=48)
void eat(String food){//default
System.out.println("我爱吃----"+food);
}
}
class SonTest extends FatherTest{
//属性区
public static String name;
private int age;
String gender;
//构造方法区
public SonTest(String name, int age) {
this.name = name;
this.age = age;
}
private SonTest(String name) {
this.name = name;
}
//方法区
@MySon(name="李二狗",age=18)
void eat(String food){//default
System.out.println("我爱吃----"+food);
}
public String show(String show,int age){
return "我"+age+"岁就会表演---"+show;
}
protected int getAge(){
return 19;
}
private static void call(){
System.out.println("有事给我打电话");
}
}
测试代码---------------获取属性相关信息
class Test{
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
Class cla=Class.forName("com.gavin.flect.SonTest") ;
//获得属性
Field name = cla.getField("name");//getField()只能获得public修饰的属性
System.out.println(name);
System.out.println(name.getName());//获取属性名
int modifiers = name.getModifiers();//获取属性修饰符
System.out.println(Modifier.toString(modifiers));//打印修饰符
System.out.println("1-----------------");
Field[] shuxing = cla.getDeclaredFields();//getDeclaredFields可获得所有
for ( Field f:shuxing
) {
System.out.println(f.toString());
}
}
}
测试代码---------------获取方法相关信息
class Test{
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
Class cla=Class.forName("com.gavin.flect.SonTest") ;
//获取方法
Method[] dMethods = cla.getMethods();//获取当前类中的所有被public修饰的方法包括父类中的被public修饰的
for (Method d:dMethods
) {
System.out.println(d);
}
System.out.println("1----------------------");
Method show = cla.getMethod("show", String.class, int.class);//getMethod只能获得public修饰的方法
System.out.println(show);
int modifiers = show.getModifiers();//获得方法的修饰符
System.out.println(Modifier.toString(modifiers));//打印修饰符
System.out.println("2----------------------");
Class cla1=Class.forName("com.gavin.flect.SonTest") ;
Method[] declaredMethods1 = cla1.getDeclaredMethods();//获得当前类中的所有方法---不包括父类
for (Method d :
declaredMethods1) {
System.out.println(d);
}
}
}
测试代码---------------获取注解相关信息
class Test {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
Class cla = Class.forName("com.gavin.flect.SonTest");
Method eat = cla.getDeclaredMethod("eat", String.class);
Annotation[] declaredAnnotations = eat.getDeclaredAnnotations();//得到该方法上的所有注解----RetentionPolicy.RUNTIME条件下的
for (Annotation an :
declaredAnnotations) {
System.out.println(an);
}
Annotation annotation = eat.getAnnotation(MySon.class);
System.out.println(annotation);
}
}
}
在SonTest类中补充方法funmethod()----获取参数的注解----
private void funmethod(@MySon int a,@Myfather(height = 188.8) double b){
}
测试类—
class Test {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
Class cla = Class.forName("com.gavin.flect.SonTest");
Method funmethod = cla.getDeclaredMethod("funmethod", int.class, double.class);
Annotation[][] parameterAnnotations = funmethod.getParameterAnnotations();
for (Annotation [] p:parameterAnnotations
) {
for (Annotation pp:p
) {
System.out.println(pp);
}
}
}
}
通过反射调用方法-----
class Test {
public static void main(String[] args) throws Exception {
Class cla = Class.forName("com.gavin.flect.SonTest");//加载类字节码信息
Constructor de = cla.getDeclaredConstructor(String.class);//获得构造方法
de.setAccessible(true);//私有构造要设置权限可访问
Object o = de.newInstance("李二狗");//实例化----可以转型也可以不转
Method eat=cla.getDeclaredMethod("eat", String.class);//获得方法
eat.invoke(o,"鱼");//运行方法----invoke(对象,方法参数)
}
}
未完待续…21.8.26