由关闭通知导致Toast无法正常显示到Java中的反射

2 篇文章 0 订阅

由关闭通知导致Toast无法正常显示到Java中的反射

遇到的问题

在Android开发中遇见因为关闭了消息通知导致Toast无法正常使用,所以采取了自己维护消息队列的方法来解决这个算是系统的小bug。
在解决的过程中用到了反射的东西,因为AppOpsManager的方法是私有的。(好多Android中sdk的api的方法是私有的所以我们不能通过new的方式得到类的实例所以只能这么做了)

说说java中的反射机制

一.概述

反射机制其实是java中的,通过它我们可以做到:
1.得到这个类的实例
2.操作这个类中包含的方法
3.操作这个类中的变量
当然最好还是使用new的方法来得到实例,因为性能不如new的方法。
反射机制的利弊
其实好处就是,增加程序的灵活性,避免将程序写死到代码里;
但是坏处也有,就是性能是一个问题,反射相当于一系列解释操作,通知jvm要做的事情,性能比直接的java代码要慢很多。且不安全,通过反射机制我们能拿到类的私有成员。

二.详解

Reflection。这个字的意思是“反射、映象、倒影”,用在Java身上指的是我们可以于运行时加载、探知、使用编译期间完全未知的classes。换句话说,Java程序可以加载一个运行时才得知名称的class,获悉其完整构造(但不包括methods定义),并生成其对象实体、或对其fields设值、或唤起其methods。这种“看透class”的能力(the ability of the program to examine itself)被称为introspection(内省、内观、反省)。
Reflection和introspection是常被并提的两个术语。
反射之中包含了一个“反”的概念,所以要想解释反射就必须先从“正”开始解释,一般而言,当用户使用一个类的时候,应该先知道这个类,而后通过这个类产生实例化对象,但是“反”指的是通过对象找到类。

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    showTv = (TextView) findViewById(R.id.show);
    show();
}
private void show() {
    Person person = new Person();// 正着操作
    String personName = person.getClass().getName(); // 反着来
    showTv.setText(personName);
}

以上的代码使用了一个getClass()方法,而后就可以得到对象所在的“包.类”名称,这就属于“反”了,但是在这个“反”的操作之中有一个getClass()就作为发起一切反射操作的开端。Person的父类是Object类,而上面所使用getClass()方法就是Object类之中所定义的方法。
取得Class对象:

public final Class<?> getClass()

反射之中的所有泛型都定义为?,返回值都是Object。而这个getClass()方法返回的对象是Class类的对象,所以这个Class就是所有反射操作的源头。但是在讲解其真正使用之前还有一个需要先解释的问题,既然Class是所有反射操作的源头,那么这个类肯定是最为重要的,而如果要想取得这个类的实例化对象,Java中定义了三种方式:

方式一:通过Object类的getClass()方法取得,基本不用:
private void show() {
    Person person = new Person();
    Class<?> class1 = person.getClass();
    showTv.setText(class1.getName());
}
方式二:使用“类.class”取得
private void show() {
    Class<?> class1 = Person.class;
    showTv.setText(class1.getName());
}
方式三:使用Class类内部定义的一个static方法,主要使用取得Class类对象:
public static Class<?> forName(String className) throws ClassNotFoundException;
使用的代码:
private void show() {
    Class<?> class1;
    try {
        class1 = Class.forName("com.gong.test.Person");
        showTv.setText(class1.getName());
    } catch (ClassNotFoundException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

}

那么现在一个新的问题又来了,取得了Class类的对象有什么用处呢?用处就是对于对象的实例化操作,之前一直依靠构造方法和关键字new完成,可是有了Class类对象之后,现在又提供了另外一种对象的实例化方法:通过反射实例化对象:

public T newInstance() throws InstantiationException, IllegalAccessException;

范例:通过反射实例化对象

private void show() {
    Class<?> class1;
    try {
        class1 = Class.forName("com.gong.test.Person");// 取得Class对象
        Person person = (Person) class1.newInstance();// 实例化对象,和使用关键字new一样;同时向下强转为Person类
    } catch (ClassNotFoundException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }...省略各种异常捕获

}
通过反射对普通工厂模式的修改

那么现在可以发现,对于对象的实例化操作,除了使用关键字new之外又多了一个反射机制操作,而且这个操作要比之前使用的new复杂一些,可是有什么用?对于程序的开发模式之前一直强调:尽量减少耦合,而减少耦合的最好做法是使用接口,但是就算使用了接口也逃不出关键字new,所以实际上new是造成耦合的关键元凶。
范例:回顾一下之前所编写的工厂设计模式(最一般的)

接口
public interface Animal {

    void eat();
}
第一个实现类
public class Cat implements Animal{

    @Override
    public void eat() {
        Log.e("Cat", "吃鱼");
    };

}
第二个实现类
public class Cow implements Animal{

    @Override
    public void eat() {

        Log.e("Cow", "吃草");
    }
}
工厂类
public class AnimalFactory {

    public static Animal getAnimal(String name){
        if (name.equals("cat")) {
            return new Cat();
        }else if (name.equals("cow")) {
            return new Cow();
        }
        return null;
    }
}
使用
private void show() {
    Animal animal = AnimalFactory.getAnimal("cat");
    animal.eat();
    Animal animal2 = AnimalFactory.getAnimal("cow");
    animal2.eat();
}

以上为之前所编写最简单的工厂设计模式,但是在这个工厂设计模式之中有一个最大的问题:如果现在接口的子类增加了,那么工厂类肯定需要修改,这是它所面临的最大问题,而这个最大问题造成的关键性的病因是new,那么如果说现在不使用关键字new了,变为了反射机制呢?
反射机制实例化对象的时候实际上只需要“包.类”就可以,于是根据此操作,修改工厂设计模式。

改造工厂
public class AnimalFactory {
    @SuppressLint("NewApi")
    public static Animal getAnimal(String name){
        try {
            Animal animal = (Animal) Class.forName(name).newInstance();
            return animal;
        } catch (InstantiationException | IllegalAccessException
                | ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return null;
    }
}

调用也改了
private void show() {
    Animal animal = AnimalFactory.getAnimal("com.gong.test.Cat");
    animal.eat();
    Animal animal2 = AnimalFactory.getAnimal("com.gong.test.Cow");
    animal2.eat();
}

发现,这个时候即使增加了接口的子类,工厂类照样可以完成对象的实例化操作,这个才是真正的工厂类,可以应对于所有的变化。如果单独从开发角度而言,与开发者关系不大,但是对于日后学习的一些框架技术这个就是它实现的命脉,在日后的程序开发上,如果发现操作的过程之中需要传递了一个完整的“包.类”名称的时候几乎都是反射机制作用。

三.反射的深入应用

以上只是利用了Class类作为了反射实例化对象的基本应用,但是对于一个实例化对象而言,它需要调用类之中的构造方法、普通方法、属性,而这些操作都可以通过反射机制完成。

3.1 调用构造方法

使用反射机制也可以取得类之中的构造方法,这个方法在Class类之中已经明确定义了:
以下两个方法:

取得一个类的全部构造:
public Constructor<?>[] getConstructors() throws SecurityException
取得一个类的指定参数构造:
public Constructor<T> getConstructor(Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException

现在发现以上的两个方法返回的都是java.lang.reflect.Constructor类的对象。
范例:取得一个类之中的全部构造

public class Person {
    private String name;
    private int id;
    public Person(){

    }
    public Person(int id){

    }
    public Person(String name, int id){

    }
}
private void show() {
    try {
        Class<?> class1 = Class.forName("com.gong.test.Person");
        Constructor<?> cos[] = class1.getConstructors();
        for (int i = 0; i < cos.length; i++) {

        }
    } catch (ClassNotFoundException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}

注意:这里要注意在写普通类Person的时候加上无参构造,否则通过反射来取实例的时候会报“java.lang.InstantiationException”;
范例:取得一个类之中某个构造方法,并得到实例

无参:
private void show() {
    try {
        Class<?> class1 = Class.forName("com.gong.test.Person");
        Constructor<?> constructor = class1.getConstructor();
        Person person = (Person) constructor.newInstance();
    } catch (ClassNotFoundException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } ...省略各种异常捕获
}
有参:
private void show() {
    try {
        Class<?> class1 = Class.forName("com.gong.test.Person");
        Constructor<?> constructor = class1.getConstructor(String.class, int.class);
        Person person = (Person) constructor.newInstance("张三", 13);
    } catch (ClassNotFoundException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } ...省略各种异常捕获
}

很明显,调用无参构造方法实例化对象要比调用有参构造的更加简单、方便,所以在日后的所有开发之中,凡是有简单Java类出现的地方,都一定要提供无参构造。

3.2 调用普通方法

当取得了一个类实例化对象之后,下面最需要调用的肯定是类之中的方法,所以可以继续使用Class类取得一个类中所定义的方法定义:

取得全部方法:
public Method[] getMethods() throws SecurityException;
取得指定方法:
public Method getMethod(String name, Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException

发现以上的方法返回的都是java.lang.reflect.Method类的对象。
范例:取得一个类之中所定义的全部方法

private void show() {
    try {
        Class<?> class1 = Class.forName("com.gong.test.Person");
        Method method[] = class1.getMethods(); 
        for (int i = 0; i < method.length; i++) {

        }
    } catch (ClassNotFoundException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}

范例:取得一个类之中某个方法并调用它:

private void show() {
    try {
        String attribute = "name";
        Class<?> class1 = Class.forName("com.gong.test.Person");
        Person person = (Person) class1.newInstance();
        Method setMtd = class1.getMethod("set" + initcap(attribute), String.class);
        setMtd.invoke(person, "张三");
    } catch (ClassNotFoundException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } ...省略各种异常捕获
}
publicstaticString initcap(String str) {
    returnstr.substring(0,1).toUpperCase().concat(str.substring(1));
}
3.3调用成员

类之中最后一个组成部分就是成员(Field,也可以称为属性),如果要通过反射取得类的成员可以使用方法如下:

·取得本类的全部成员:
public Field[] getDeclaredFields() throws SecurityException;
·取得指定的成员:
public Field getDeclaredField(String name) throws NoSuchFieldException, SecurityException;

这两个方法的返回值类型是java.lang.reflect.Field类的对象,下面首先观察如何取得一个类之中的全部属性。
范例:取得一个类之中的全部属性

private void show() {
    try {
        String attribute = "name";
        Class<?> class1 = Class.forName("com.gong.test.Person");
        Field field[] = class1.getDeclaredFields();
        for (int i = 0; i < field.length; i++) {

        }
    } catch (ClassNotFoundException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } 
}

但是找到Field实际上就找到了一个很有意思的操作,在Field类之中提供了两个方法:

·设置属性内容(类似于:对象.属性= 内容):
public void set(Object obj, Object value)
throws IllegalArgumentException, IllegalAccessException;
·取得属性内容(类似于:对象.属性):
public Object get(Object obj)
throws IllegalArgumentException, IllegalAccessException

可是从类的开发要求而言,一直都强调类之中的属性必须封装,所以现在调用之前要想办法解除封装。

·解除封装:public void setAccessible(boolean flag) throws SecurityException;

范例:利用反射操作类中的属性

private void show() {
    try {
        String attribute = "name";
        Class<?> class1 = Class.forName("com.gong.test.Person");// 取得Class对象
        Object obj = class1.newInstance();// 对象实例化属性才会分配空间
        Field field = class1.getDeclaredField("name");// 找到name属性
        field.setAccessible(true);// 解除封装了
        field.set(obj, "张三");// Person对象.name = "张三"
    } catch (ClassNotFoundException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } ...省略各种异常捕获
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值