首页 > 移动平台 > 详细

android日记(二)

时间:2020-05-22 21:17:16      阅读:69      评论:0      收藏:0      [点我收藏+]

11.多线程中的空指针问题

  • 导致问题的方法如下,在判空后,却得到model对象抛NullPointerException。
    private void work() {
        Log.d("zouhecan", getThreadId() + "start work()");
        if (model != null) {
            Log.d("zouhecan", getThreadId() + "id=" + model.getId());
            Log.d("zouhecan", getThreadId() + "name=" + model.getName());//model throw NullPointerException
            Log.d("zouhecan", getThreadId() + "flag=" + model.getFlag());
        }
    }
  • 首先定位问题为:多线程操作model对象,使得其中一个线程判model不为空后,另一个线程又置model=null,这时线程操作work方法还未结束,仍然读操作了model对象。
    private Runnable workRunnable = new Runnable() {
        @Override
        public void run() {
            try {
                work();
                Thread.sleep(1000);
            } catch (Exception e) {
                Log.e("zouhecan", e.getMessage());
            }
        }
    };
    
    private Runnable resetRunnable = new Runnable() {
        @Override
        public void run() {
            Log.d("zouhecan", getThreadId() + "model=null");
            model = null;
        }
    };
  • 【误解】有人说对用关键字synchornized声明work()为同步方法。这样只是保持所有操作work()方法的线程同步,不
    private synchronized void work() {
        ...
    }

12.使用反射

  • 反射是在运行时,能够动态获取和调用一个类的所有属性、方法,的一种机制。
  • 反射获取类信息,使用Class.forName()方法,需要捕获ClassNotFoundException。Throwable家族有Error【StackOverFlow、OutOfMemory】和Exception两大分支,Exception又氛围RunTimeException【NullPointer、IIegalArgument、ArrayIndexOutOfBound等】和非运行时异常(在编译检查时)【ClassNotFound,Interrupter,IOException等】。
    try {
        Class clazz = Class.forName("com.example.fragmenttest.TestClass");
        testReflection(clazz);
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }
  • 反射获取构造函数,其中getConstructors()只获取public构造函数,getDeclaredConstructors()则获取所有的构造函数。
    //获取public构造函数
    Constructor[] constructors = clazz.getConstructors();
    
    
    //获取所有构造函数(public,private,protected)
    Constructor[] declaredConstructors = clazz.getDeclaredConstructors();
  • 反射创建对象,先通过getConstructor()根据参数表类型选择对应构造函数,执行constructor.newInstance()传入参数参数表value,并创建对象
    @SuppressWarnings("unchecked")
    Constructor constructor = clazz.getDeclaredConstructor(String.class, int.class);
    constructor.setAccessible(true);
    TestClass testClass = (TestClass) constructor.newInstance("反射调用构造函数", 1);//创建对象
    
    public class TestClass {
        private TestClass(String message, int mode) {
            Log.d("zouhecan", message);
        }
        ...
    }
  • 反射获取Field,getFields()获取public成员,getDeclaredFields()获取全部成员。通过clazz.getField(fieldName)可以获取指定域名的成员(public),非public成员只能通过clazz.getDecalredField(fieldName)获取,否则会抛NoSuchFieldException。如果要操作某个成员,需要先获得对象实例intance【可通过getConstructor().getInstance()得到】,设置field值:field.set(instance, value)给field设置实参value;获取filed值:field.get(instance)将结果强转成对应类型。注意:如果操作的field是private修饰的,需要执行field.setAccessible(true),解除私有限制,否则会抛IlegalAccessException,更进一步地,就算不是操作的不是private成员,也有必要设置accessible=true,这样可以绕过私有检查,操作速度将提升20倍。
    private void testFields(Class clazz){
            //获取public字段
            Field[] fields = clazz.getFields();
    
            //获取所有字段
            Field[] declaredFields = clazz.getDeclaredFields();
    
            for (Field field : fields) {
                Log.d("zouhecan", field.getName());
            }
            
            for (Field field : declaredFields) {
                Log.d("zouhecan", field.getName());
            }
        }
    try {
                TestClass instance = (TestClass) clazz.getConstructor().newInstance();
    
                //反射设置filed值
                Field numberField = clazz.getField("number");//number是public field
                numberField.set(instance, 1);
    
                //反射获取filed值
                Field nameField = clazz.getDecarledField("name");//name是private field
                nameField.setAccessible(true);//解除私有限制
                String nameValue = (String) nameField.get(instance);
    
            } catch (Exception e) {
                e.printStackTrace();
            } 
  • 反射调用method,与操作field类似,getMethods()获取public方法(包含父类public方法),getDeclaredMethods()获取全部方法(自己申明的,不包括继承的)。通过getMethod("methodName")获取指定方法名的方法(public),通过getDeclaredMethod("methodName")获取指定的非public方法。再创建类实例instance后,就可以通过method.invoke(instance)调用方法。如果方法是含参的,则在反射获取方法时,需要指明方法参数,method.getDeclaredMethod("method", Object.class),并在调用时指明实参method.invoke(instance, ‘params‘)。

     @SuppressWarnings("unchecked")
        private void testMethod(Class clazz) {
            //获取public方法
            Method[] methods = clazz.getMethods();
            //获取全部方法
            Method[] declaredMethods = clazz.getDeclaredMethods();
            try {
                //创建实例
                TestClass instance = (TestClass) clazz.getConstructor().newInstance();
                //获取public方法
                Method mTestReflect = clazz.getMethod("testReflect");
                //获取非public方法
                Method mInit = clazz.getDeclaredMethod("init");
                //获取一个名为test,参数为string类型的方法
                Method mTest = clazz.getDeclaredMethod("test", String.class);
                //调用方法
                mTestReflect.invoke(instance);
                mInit.invoke(instance);
                mTest.invoke(instance, "test invoke method by reflect");//调用方法,并传入方法实参
            } catch (InstantiationException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
                e.printStackTrace();
            }
        }
  • 反射能不能修改final常量?答案:当然能,final是编译时的标记,反射是在运行时修改,反射能实现范型擦除也是这个原因。然而要注意,修改后的私有常量,调用方拿到的结果可能还是没有修改的。看了下面的代码,你就会明白一切。参考:https://juejin.im/post/598ea9116fb9a03c335a99a4
    1.定义常量
    public
    class TestClass { //String 会被 JVM 优化 private final String FINAL_VALUE = "FINAL"; public String getFinalValue() { //剧透,会被优化为: return "FINAL" ,拭目以待吧 return FINAL_VALUE; } }
     2.反射修改常量
    /** * 修改对象私有常量的值 * 为简洁代码,在方法上抛出总的异常,实际开发别这样 */ private static void modifyFinalFiled() throws Exception { //1. 获取 Class 类实例 TestClass testClass = new TestClass(); Class mClass = testClass.getClass(); //2. 获取私有常量 Field finalField = mClass.getDeclaredField("FINAL_VALUE"); //3. 修改常量的值 if (finalField != null) { //获取私有常量的访问权 finalField.setAccessible(true); //调用 finalField 的 getter 方法 //输出 FINAL_VALUE 修改前的值 System.out.println("Before Modify:FINAL_VALUE = " + finalField.get(testClass)); //修改私有常量 finalField.set(testClass, "Modified"); //调用 finalField 的 getter 方法 //输出 FINAL_VALUE 修改后的值 System.out.println("After Modify:FINAL_VALUE = " + finalField.get(testClass)); //使用对象调用类的 getter 方法 //获取值并输出 System.out.println("Actually :FINAL_VALUE = " + testClass.getFinalValue()); } }
    3.结果
    Before Modify:FINAL_VALUE = FINAL
    After Modify:FINAL_VALUE = Modified
    Actually :FINAL_VALUE = FINAL

    结果出来了:

    第一句打印修改前 FINAL_VALUE 的值,没有异议;

    第二句打印修改后常量的值,说明FINAL_VALUE确实通过反射修改了;

    第三句打印通过 getFinalValue() 方法获取的 FINAL_VALUE 的值,但还是初始值,导致修改无效!不信的话,看看字节码吧。

    技术分享图片

13.范型

  • 范型类
  • 范型方法
  • Class<T>与Class<?>区别

14.注解自动生成器

  • AbstractAnnotationProcess

15.坚持记录吧

  • 未来的你会感激现在的自己

android日记(二)

原文:https://www.cnblogs.com/zhc-android-blogger/p/12939332.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!