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(); } }
13.范型
- 范型类
- 范型方法
- Class<T>与Class<?>区别
14.注解自动生成器
- AbstractAnnotationProcess
15.坚持记录吧
- 未来的你会感激现在的自己