分类目录归档:Java

Java 设计模式总结

Published / by sickworm / Leave a Comment

创建类模式

1. 单例模式

特点:全局唯一,所有类共享一个实例化对象

使用场景:一个类独占一个唯一资源时考虑使用。如蓝牙等 IO 类,或者根,管理器等虚拟类。其他情况应慎重使用。

2. 工厂模式

特点:用户无需知道实例构造方法,方便用户实例化

使用场景:直接实例化一个对象较为复杂(需要多步)时;一个对象(接口)对应多种实现,而用户无需关注具体实现类是哪个时。

3. 抽象工厂模式

特点: 工厂的工厂

使用场景:存在多个平行的工厂模式实现类时。

4. 建造者模式

特点: 一个 Builder 类对应一个类,通过链式调用不断添加参数,在最后调用 build() 实例化对应类。

使用场景:创建一个对象比较复杂,且某些设置可选时。

5. 原型模式

特点:输入一个实例(称为原型),返回一个实例的复制。

使用场景:克隆一个对象,或创建一个模拟这个对象的行为的对象。

行为类模式

6. 模板方法模式

特点:即 Java 抽象类

使用场景:需要把策略抽象出来,但又与具体实现密切相关时可用。抽象类其实不是必须的,可以用组合代替(把策略抽取为一个单独的类)。

7. 中介者模式

特点:俗话说的:封一层。用于精简接口和类之间的操作。

使用场景:某个流程较为复杂,涉及多个类的交互。

8. 观察者模式

特点:观察类设置监听器给被观察类,在观察类发生了监听器想监听的事件时,被观察类主动调用观察类的监听器。

使用场景:凡是需要关注事件的,都可以使用。

9. 访问者模式

特点: 将被访问类对象传入到访问类,访问类调用这个对象

使用场景:一个对象中存在着一些与本对象不相干(或者关系较弱)的操作,为了避免这些操作污染这个对象,则可以使用访问者模式来把这些操作封装到访问者中去。

10. 命令模式

特点: 发送者对象发送请求对象,接收者对象返回结果。请求对象内含实现方法(Runnable 算得上是一种命令模式?)。调用,接收和实现分散 在 3 个类中。

使用场景:一个请求一个响应的模式比较适合命令模式。

11. 责任链模式

特点:事件(请求)处理类(Handler)对象连成一条链,链的顺序表示优先级。事件到达时按优先级流水线处理。

使用场景:一个请求可能被多个不同的对象接收,但存在接收优先级时。如多层 UI 布局的点击事件。

12. 策略模式

特点:一个算法接口或类,被多个子类实现/继承。各个子类可以根据需求互相替换。即:多种算法,处理一种数据。

使用场景:多个子类继承于同一个父类/接口,基本就算策略模式。

13. 解释器模式

特点:解析特定格式数据,并执行数据所代表的操作。通常使用状态机实现。

使用场景:需要创造并解析自定义数据格式时。

14. 备忘录模式

特点:每一步都被记录,操作可还原。常见实现有 log 记录,merkle 树。

使用场景:当操作有回滚的需求时。

版权所有,转载请注明出处:
https://sickworm.com/?p=1699

Java右移每32位(64位)循环

Published / by sickworm / Leave a Comment
        long a = 33;
        for (int i = 0; i < 64; i++) {
            System.out.println(i + " " + (a >> i));
        }

结果:

0 33
1 16
2 8
3 4
4 2
5 1
6 0
7 0
...
62 0
63 0

而如果是int, short, byte:

        int a = 33; // short, byte 也一样
        for (int i = 0; i < 64; i++) { // i 为 short, byte也一样
            System.out.println(i + " " + (a >> i));
        }

结果是:

0 33
1 16
2 8
3 4
4 2
5 1
6 0
7 0
...
30 0
31 0
32 33
33 16
34 8
35 4
36 2
37 1
38 0
39 0
40 0
41 0
...
62 0
63 0
  • 为何出现这样的情况?

  • 猜测是机器码只会截取低8bit/4bit作为移位量。(需反编译,待续)

  • 为何int short boolean和long不一样?

  • Java运算时默认将操作数扩展为int(long位数大于int所以不会变化),所以int,short,boolean结果都是32位一次循环。

版权所有,转载请注明出处:
https://sickworm.com/?p=486

Android/Java 混淆中使用-assumenosideeffects删除日志代码遇到的问题

Published / by sickworm / Android/Java 混淆中使用-assumenosideeffects删除日志代码遇到的问题有1条评论

今天发包给客户,发现混淆后的库时序有点问题。再三调试,发现锁失效了。wait()没有任何阻塞就跳过了。

ok,90%情况就是在哪里触发了notify/notifyAll咯。但找了很久,notify确实没有被调用。我就纳闷了。

最后我把我的库反编译出来看,发现我的锁的wait()语句被删了!源代码:EsLock.java

public void lock()
{
    synchronized (this) {
        setLocked(true);
        try {
            Log.e("wtf", "" + 1);
            wait();
            Log.e("wtf", "" + 2);
        } catch (Exception e) {
            e.printStackTrace();
            LogUtil.w(TAG, e.getLocalizedMessage());
        }
        setLocked(false);
    }
}

混淆后代码:
https://i0.wp.com/sickworm.com/wp-content/uploads/2016/11/tmp6d1fa6b6.png?w=960

我翻了一下我CI上的库记录,发现前两个月的库是没问题的,看来是中间某段时间修改混淆脚本出了问题。

一番定位,找到了元凶:

-assumenosideeffects class com.excelsecu.driver.util.LogUtil {
    public *;
}

我使用了assumenosideeffects,并尝试将所有com.excelsecu.driver.util.LogUtil的调用删除。官方有关assumenosideeffects的介绍:http://proguard.sourceforge.net/manual/usage.html

assumenosideeffects需要你自己保证你所选择的类的方法没有边界效应(简单来说就是删掉也不会影响程序运行),然后proguard会帮你删掉这些方法的调用。典型例子就是打包时删掉日志输出。官方例子:http://proguard.sourceforge.net/manual/examples.html#logging

需要注意的是:他只会删除这个方法的调用,但是你如何构建你的日志内容(表现形式为StringBuilder)仍然会保留下来。你无法通过这个方法完全删掉你日志的痕迹,以用于保护代码。为什么这样做?因为如果有个傻子图方便直接在log参数里面调用了有边界效应的方法(也就是流程中必不可缺的方法),那你删掉就要出事了。

回到正题:官方给出的示例其实是没有这样的用法,只有填写特定方法的用法。但是这个标签也是支持通配符的,官方对其定义是“-assumenosideeffects class_specification”,class_specification描述了你可以如何描述这个class中的方法。和-keepclass是一样的。这个用法我是在http://stackoverflow.com/questions/6408574/how-to-use-assumenosideeffects-class-android-util-log-in-my-app最下面看到的。

我用回官方的写法:

-assumenosideeffects class com.excelsecu.driver.util.LogUtil {
    public static void v(...);
    public static void i(...);
    public static void w(...);
    public static void d(...);
    public static void e(...);
}

问题没有出现。

所以问题就在于:使用了通配符“public *”之后,proguard把LogUtil之外的方法删了,例如我的EsLock.java中的wait()的调用。(因为这个调用没有返回值,proguard认为是没有边界效应的)

综合来说,我觉得依然是个bug,因为无论如何它不应该把LogUtil之外的方法也删掉。我在https://sourceforge.net/p/proguard/bugs/629/上提交了bug(语法错误好多。。),暂时没有回复。


提交bug后第二天就收到了回复。项目人员给出了相关解释,大概结论就是:这不是bug,proguard是设计成这个样子的。

项目人员提供的相关内容:

http://proguard.sourceforge.net/中:Troubleshooting > “Note: the configuration specifies that none of the methods of class ‘…’ have any side effects”。

简单解释一下:
proguard的混淆是需要往上寻找父类的方法的,所以通配符*也会包括父类的方法。而所有的类都继承与Object,自然Object.wait()和Object.notify()也会在检测列表中。所以当你使用了统配符的时候,这两个方法也是会被影响的。

那么问题来了,为什么不是LogUtil.wait()这样的调用才会被删除,而是EsLock.wait()的方法也会被删除?我猜测proguard采用的是一种展开的方式去处理的,当你配置了LogUtil的所有方法时,他会同时产生一个Object的所有方法的配置。这样处理起来会高效很多。

class specifications是一个统一的定义,-keep等配置也会用到。所以可能很难兼顾所有配置项的使用场景。

官方文档也明确说明了,最好别在assumenosideeffects中使用通配符,这样会影响到wait和notify。

版权所有,转载请注明出处:
http://sickworm.com/?p=259