java--反射机制

1.概念 反射reflect

主要是指程序可以访问,检测和修改它本身状态或行为的一种能力,并能根据自身行为的状态和结果,调整或修改应用所描述行为的状态和相关的语义。

反射机制就是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

用一句话总结就是反射可以实现在运行时可以知道任意一个类的属性和方法。


2.为什么要使用反射,它的作用是什么,它在实际的编程中有什么应用

首先我们了解下静态编译和动态编译

静态编译:在编译时确定类型,绑定对象,即通过。
动态编译:运行时确定类型,绑定对象。动态编译最大限度发挥了java的灵活性,体现了多态的应用,有以降低类之间的藕合性。

优点

可以实现动态创建对象和编译,体现出很大的灵活性,特别是在J2EE的开发中它的灵活性就表现的十分明显。比如,一个大型的软件,不可能一次就把把它设计的很完美,当这个程序编译后,发布了,当发现需要更新某些功能时,我们不可能要用户把以前的卸载,再重新安装新的版本,假如这样的话,这个软件肯定是没有多少人用的。采用静态的话,需要把整个程序重新编译一次才可以实现功能的更新,而采用反射机制的话,它就可以不用卸载,只需要在运行时才动态的创建和编译,就可以实现该功能。

缺点

对性能有影响。使用反射基本上是一种解释操作,我们可以告诉JVM,我们希望做什么并且它满足我们的要求。这类操作总是慢于只直接执行相同的操作。
//水果
public interface fruit {
    public abstract void eat();  
}

//苹果类实现水果接口
public class Apple implements fruit{  
    public void eat(){  
        System.out.println("Apple");  
    }  
}

//橘子类实现水果接口
public class Orange implements fruit{  
    public void eat(){  
        System.out.println("Orange");  
    }  
}  
// 构造工厂类  
// 也就是说以后如果我们在添加其他的水果实例的时候只需要修改工厂类就行了   
public class Factory { 
    public static fruit getInstance(String fruitName){  
        fruit f = null;  
        if("Apple".equals(fruitName)){  
            f = new Apple();  
        }  
        if("Orange".equals(fruitName)){  
            f = new Orange();  
        }  
        return f;  
    }  
} 
//使用工厂类只要传入类名称就可以得到该类
public  class demo1 {  
    public static void main(String[] a){  
        fruit f=Factory.getInstance("Orange");  
        f.eat();  
    }  
}  
可以发现,每当我们要添加一种新的水果的时候,我们将不得不改变Factory中的源码,而往往改变原有正确代码是一种十分危险的行为。而且随着水果种类的增加,你会发现你的factory类会越来越臃肿
不得不说这是一种十分复杂的做法。(初学者可能会问,我们为什么不直接在main方法中new水果那,我们可能会需要getInstance方法做一些别的事情。。。所以不直接new);
而反射无疑是一种聪明的办法,看代码。
//工厂类
public class Factory1 {
    public static fruit getInstance(String ClassName){  
        fruit f = null;  
        try{  
        //使用反射机制
            f=(fruit)Class.forName(ClassName).newInstance();
        }catch (Exception e) {  
            e.printStackTrace();  
        }  
        return f;  
    }  
}
在出现新品种水果的时候,你完全不用去修改原有代码。从上面的案例中,我们可以清楚的体会到反射的优越性

3.怎么用

首先,我们要通过Class来获取一个类的实例,Class 相当于抽象了类,它的实例对象是一个个的类,这一个个的类都有类名,属性方法等等,所以就抽象出了一个Class类来管理
Class 没有公共构造方法。Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的。
虽然我们不能new一个Class对象,但是却可以通过已有的类得到一个Class对象,共有三种方式,如下:
package reflect;

public class Demo {
    //代码块
}
package reflect;

public class test {
    public static void main(String[] args) {
        Class<?> demo1 = null;
        Class<?> demo2 = null;
        Class<?> demo3 = null;

        try {
            //一般采用这种方式
            demo1 = Class.forName("reflect.Demo");
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        demo2 = new Demo().getClass();

        demo3 = Demo.class;

        System.out.println("类名称 "+demo1.getName());
        System.out.println("类名称 "+demo2.getName());
        System.out.println("类名称 "+demo3.getName());
    }
}
运行结果:
类名称 reflect.Demo
类名称 reflect.Demo
类名称 reflect.Demo
前面我们知道了怎么获取Class,那么我们可以通过这个Class干什么呢?

总结如下:

1.获取成员方法Method

2.获取成员变量Field

3.获取构造函数Constructor

1.获取成员方法Method

    public Method getDeclaredMethod(String name, Class<?>... parameterTypes) // 得到该类所有的方法,不包括父类的 
    public Method getMethod(String name, Class<?>... parameterTypes) // 得到该类所有的public方法,包括父类的

【案例】

package reflect;
//Person类
public class Person {
    private String name;
    private int age;
    private String msg = "hello world";
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getMsg() {
        return msg;
    }
    public void setMsg(String msg) {
        this.msg = msg;
    }
    public Person(){

    }
    private Person(String name){
        this.name = name;
        System.out.println("姓名为:"+name);
    }
    public void fun(){
        System.out.println("fun");
    }
    public void fun(String name,int age) {
        System.out.println("我叫"+name+",今年"+age+"岁");
    }
}
public class demo2 {
    public static void main(String[] args){
        try {
            Class c = Class.forName("reflect.Person");
            Object o = c.newInstance();//创建此 Class 对象所表示的类的一个新实例。
            Method method = c.getMethod("fun", String.class, int.class);
            method.invoke(o, "zhangsan", 10);// 对带有指定参数的指定对象调用由此 Method 对象表示的底层方法。
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
运行结果:
我叫zhangsan,今年10岁

获取类种所有的方法

public class demo3 {
     public static void main(String[] args){
            try {
                Class c = Class.forName("reflect.Person");
                Method[] methods = c.getDeclaredMethods();
                //循环输出类中所有方法
                for(Method m:methods){
                    String  methodName= m.getName();
                    System.out.println(methodName);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
    }
}
运行结果:
getName
setName
getAge
setMsg
setAge
fun
fun
getMsg

2.获取成员变量Field

public Field getDeclaredField(String name) // 获得该类自身声明的所有变量,不包括其父类的变量
public Field getField(String name) // 获得该类自所有的public成员变量,包括其父类变量

【案例】

public class demo4 {
    public static void main(String[] args) {
        try {
             Class c = Class.forName("reflect.Person");
             //获取成员变量
             Field field = c.getDeclaredField("msg"); //因为msg变量是private的,所以不能用getField方法
             Object o = c.newInstance();
             field.setAccessible(true);//设置是否允许访问,因为该变量是private的,所以要手动设置允许访问,如果msg是public的就不需要这行了。
             Object msg = field.get(o);
             System.out.println(msg);
         } catch (Exception e) {
             e.printStackTrace();
         }
    }
}
运行结果:
hello world

如果想要获取所有成员变量的信息,可以通过以下几步

1.获取所有成员变量的数组:
Field[] fields = c.getDeclaredFields();
2.遍历变量数组,获得某个成员变量field
for (Field field : fields)  
public class demo5 {
    public static void main(String[] args){
        try {
            Class c = Class.forName("reflect.Person");
            Field[] fields = c.getDeclaredFields();
            for(Field field :fields){
                System.out.println(field.getName());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
运行结果:
name
age
msg

3.获取构造函数Constructor

public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) //  获得该类所有的构造器,不包括其父类的构造器
public Constructor<T> getConstructor(Class<?>... parameterTypes) // 获得该类所以public构造器,包括父类

【案例】

public class demo6 {
     public static void main(String[] args){
            try {
                Class c = Class.forName("reflect.Person");
                //获取构造函数
                Constructor constructor = c.getDeclaredConstructor(String.class);
                constructor.setAccessible(true);//设置是否允许访问,因为该构造器是private的,所以要手动设置允许访问,如果构造器是public的就不需要这行了。
                constructor.newInstance("zhangsan");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
}
运行结果
姓名为:zhangsan
获取所有的构造函数,可以通过以下步骤实现:
1.获取该类的所有构造函数,放在一个数组中:
Constructor[] constructors = c.getDeclaredConstructors();
2.遍历构造函数数组,获得某个构造函数constructor:
for (Constructor constructor : constructors)
public class demo7 {
    public static void main(String[] args){
        Class c;
        try {
        c = Class.forName("reflect.Person");
        Constructor[] constructors = c.getDeclaredConstructors();
            for(Constructor constructor:constructors){
                System.out.println(constructor);
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

    } 
}
运行结果
public reflect.Person()
private reflect.Person(java.lang.String)
当我们把Person中的默认的无参构造函数取消的时候,比如自己定义只定义一个有参数的构造函数之后,会出现错误,所以大家以后再编写使用Class实例化其他类的对象的时候,一定要自己定义无参的构造函数

4.其他方法

注解需要用到的

Annotation[] annotations = (Annotation[]) class1.getAnnotations();//获取class对象的所有注解 
Annotation annotation = (Annotation) class1.getAnnotation(Deprecated.class);//获取class对象指定注解 
Type genericSuperclass = class1.getGenericSuperclass();//获取class对象的直接超类的 
Type Type[] interfaceTypes = class1.getGenericInterfaces();//获取class对象的所有接口的type集合

获取class对象的信息

boolean isPrimitive = class1.isPrimitive();//判断是否是基础类型 
boolean isArray = class1.isArray();//判断是否是集合类
boolean isAnnotation = class1.isAnnotation();//判断是否是注解类 
boolean isInterface = class1.isInterface();//判断是否是接口类 
boolean isEnum = class1.isEnum();//判断是否是枚举类 
boolean isAnonymousClass = class1.isAnonymousClass();//判断是否是匿名内部类 
boolean isAnnotationPresent = class1.isAnnotationPresent(Deprecated.class);//判断是否被某个注解类修饰 
String className = class1.getName();//获取class名字 包含包名路径 
Package aPackage = class1.getPackage();//获取class的包信息 
String simpleName = class1.getSimpleName();//获取class类名 
int modifiers = class1.getModifiers();//获取class访问权限 
Class<?>[] declaredClasses = class1.getDeclaredClasses();//内部类 
Class<?> declaringClass = class1.getDeclaringClass();//外部类
getSuperclass():获取某类的父类  
getInterfaces():获取某类实现的接口
参考:https://www.daidingkang.cc/2017/07/18/java-reflection-annotations/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值