且听风吟

Don't panic! I'm a programmer.

使用iconv和convmv转换文本编码

| Comments

文本内容编码转换

文本显示乱码是因为文件本身的编码格式和编辑器打开文本所使用的编码格式不一致,使用iconv命令可以转换文本编码。如:

$ iconv -f coding1 -t coding2 file1 -o file2
-f:指定文件原始编码
-t:指定转换的目标编码
file1:代转换的文件
-o:指定输出文件

文件名编码转换

常常有这样的情况,将一个rar文件解压后中文的文件名显示乱码,显示”invalid encoding“,利用convmv命令可以进行转换:

$ convmv -f gbk -t utf-8 -r --notest path/to/your/file
-f:指定原始编码
-t:指定转换的目标编码
-r:如果目标文件是一个目录,递归处理目录下的文件
--notest:转换后将文件重命名,默认情况下这个命令不会重命名文件

Java动态代理

| Comments

概念

Java动态代理是代理模式的延伸,之所以称为”动态“,是因为我们不用去设计代理类,代理类的字节码在运行时被动态生成后由classloader加载进JVM后运行。
动态代理相关类有:

java.lang.reflect.Proxy  
这个类的主要作用是在运行时生成代理类的字节码,并通过这个字节码生成代理类对象供客户端使用。  

java.lang.reflect.InvocationHandler  
这个接口只定义了一个invoke方法,它是调用真实的被代理对象的地方,我们可以在这个方法里加上额外的代理逻辑,如果日志记录,访问鉴权等。  

Object invoke(Object proxy, Method method, Object[] args);

proxy参数是生成的代理类对象的引用,就像后面分析的,它的类型是$Proxy0,继承了Proxy类并实现了和被代理对象公共的业务接口。  
method是当前被调用的被代理方法。  
args是当前被调用的被代理方法的参数列表。

/images/dynamic_proxy.png

利用动态代理的机制,我们可以实现典型的AOP(Aspect-oriented programming)编程模式,实际上,这也是Spring AOP的实现原理。

举例

首先是被代理类和代理类公共的业务接口:

public interface Subject {
    void request();
}

AOP业务接口:

public interface AopLogger {
    void logBefore();

    void logAfter();
}

被代理类:

public class RealSubject implements Subject {
    public void request() {
        System.out
                .println("Processing request in real subject...");
    }
}

委托对象类:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class RequestInvocationHandler implements InvocationHandler {
    private Subject originalObj;

    private AopLogger logger;

    public RequestInvocationHandler(Subject originalObj) {
        super();
        this.originalObj = originalObj;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // do some processing before the method invocation
        System.out.println("Before requesting real subject...");
        logger.logBefore();
        // TODO:
        // how to use this proxy object?

        // invoke the method
        Object result = method.invoke(originalObj, args);

        // do some processing after the method invocation
        logger.logAfter();
        System.out.println("After requesting real subject...");
        return result;
    }

    public AopLogger getLogger() {
        return logger;
    }

    public void setLogger(AopLogger logger) {
        this.logger = logger;
    }
}

客户端测试代码:

import java.lang.reflect.Field;
import java.lang.reflect.Proxy;
import java.util.Properties;

public class Client {

    public static void main(String[] args) throws Exception {
        // ***** save proxy class into file *******
        Field field = System.class.getDeclaredField("props");
        field.setAccessible(true);
        Properties props = (Properties) field.get(null);
        props.put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
        // ****************************************

        // TODO:
        // 怎样防止客户端绕过proxy直接调用RealSubject的方法?
        Subject origObj = new RealSubject();
        Subject proxy = (Subject) getProxy(origObj);

        proxy.request();
    }

    private static Object getProxy(Subject origObj) {
        RequestInvocationHandler handler = new RequestInvocationHandler(origObj);
        handler.setLogger(new AopLogger() {

            @Override
            public void logBefore() {
                System.out.println("AOP logger working before...");
            }

            @Override
            public void logAfter() {
                System.out.println("AOP logger working after...");
            }

        });

        // 这里会通过ProxyGenerator类生成代理类的字节码,并由origObj所在的classloader加载进JVM,然后通过反射实例化出一个代理对象
        return Proxy.newProxyInstance(origObj.getClass().getClassLoader(), origObj.getClass()
                .getInterfaces(), handler);
    }

}

在Client测试代码中加上如下代码后可以将生成的代理类字节码保存到本地文件中:

  Field field = System.class.getDeclaredField("props");
  field.setAccessible(true);
  Properties props = (Properties) field.get(null);
  props.put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");

反编译这个class文件后察看可以更清楚幕后究竟做了什么:

import com.java.demo.dynamic_proxy.Subject;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class $Proxy0 extends Proxy
  implements Subject {
  private static Method m1;
  private static Method m3;
  private static Method m0;
  private static Method m2;

  public $Proxy0(InvocationHandler paramInvocationHandler)
    throws {
    super(paramInvocationHandler);
  }

  public final boolean equals(Object paramObject)
    throws {
    try {
      return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
    }
    catch (Error localError) {
      throw localError;
    }
    catch (Throwable localThrowable) {
    }
    throw new UndeclaredThrowableException(localThrowable);
  }

  public final void request()
    throws {
    try {
      // 转发给InvocationHanlder对象处理
      this.h.invoke(this, m3, null);
      return;
    }
    catch (Error localError) {
      throw localError;
    }
    catch (Throwable localThrowable) {
    }
    throw new UndeclaredThrowableException(localThrowable);
  }

  public final int hashCode()
    throws {
    try {
      return ((Integer)this.h.invoke(this, m0, null)).intValue();
    }
    catch (Error localError) {
      throw localError;
    }
    catch (Throwable localThrowable) {
    }
    throw new UndeclaredThrowableException(localThrowable);
  }

  public final String toString()
    throws {
    try {
      return (String)this.h.invoke(this, m2, null);
    }
    catch (Error localError) {
      throw localError;
    }
    catch (Throwable localThrowable) {
    }
    throw new UndeclaredThrowableException(localThrowable);
  }

  static {
    try {
      m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
      m3 = Class.forName("com.java.demo.dynamic_proxy.Subject").getMethod("request", new Class[0]);
      m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
      m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
      return;
    }
    catch (NoSuchMethodException localNoSuchMethodException) {
      throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
    }
    catch (ClassNotFoundException localClassNotFoundException) {
    }
    throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
  }
}

Reference

Pstree命令使用

| Comments

pstree命令以树状形式显示运行中的进程,使用这个命令可以方便的查看进程之间的父子关系。 一些选项:

-h:高亮当前进程
-p:显示进程pid
-u:显示uid
-a:显示启动进程的命令
-l:对长于132个字符会被截断显示,使用这个选项关闭这个功能

.xinitrc and .xprofile

| Comments

如果使用startx或者slim等命令启动X,会source ~/.xinitrc。
如果使用登录管理器GDM或者KDM,会source ~/.xprofile,忽略~/.xinitrc。

为了统一,干脆这样好了:

ln -s ~/.xinitrc ~/.xprofile

一段shell脚本的分析

| Comments

陈皓在微博上放出一段脚本,用来统计在日常工作中哪些命令用的最多:

history | awk '{CMD[$2]++;count++;} END { for (a in CMD ) print CMD[a] " " CMD[a]/count*100 "% " a }' | grep -v "./" | column -c3 -s " " -t |sort -nr | nl | head -n10

我的运行结果是这样的:

 1  421  21.05%  git
 2  232  11.6%   cd
 3  169  8.45%   ls
 4  126  6.3%    vi
 5  108  5.4%    sudo
 6  94   4.7%    man
 7  66   3.3%    fg
 8  56   2.8%    adb
 9  52   2.6%    vim
10  38   1.9%    grep

下面简单分析下这段脚本。
这段脚本分为以下几个步骤执行:

  1. 执行命令history,输出命令历史记录,输出格式为:

    number command

  2. 将step 1的输出结果交给awk脚本处理:

    awk '{CMD[$2]++;count++;} END { for (a in CMD )print CMD[ a ]" " CMD[ a ]/count*100 "% " a }' 
    

    history的输出记录首先依次交给CMD[$2]++;count++;处理。awk中,$n表示当前记录的第n个字段,字段间由FS分隔,默认为空格,$0表示完整的输入记录。此处$2表示输入记录的第2个字段,即command字段。这里定义了一个名为CMD的数组和一个count变量,其中CMD数组的index为命令字符串,value为一个整数,代表这条命令出现的次数。count变量记录了处理过的记录条数。每处理一条记录,以这条记录里的命令为index对应的value就会加1,同时count值加1。
    END模式指定了当读完所有的记录后,执行的操作。http://www.gnu.org/software/gawk/manual/gawk.html#Using-BEGIN_002fEND。这里会遍历CMD数组并打印,格式为:

    命令出现的次数 命令出现的次数所占的百分比 命令名
    2 0.1% ps
    421 21.05% git
    
  3. 将step 2的输出交由grep处理:

    grep -v "./"
    

    将把匹配”./“的记录滤掉,如”./configure”这样的不在PATH中的命令

  4. 将step 3的输出用column命令格式化对齐。
  5. 然后用sort命令排序,-n选项指定按照数字排序,-r指定倒序排序
  6. 用nl命令加上行号
  7. 用head命令取前10条记录输出

Reference

面向对象设计-原则与模式

| Comments

多态

多态是面向对象思想的三大特征中最核心的元素。多态有两方面的含义:

  1. 允许将子类类型的指针赋值给父类类型的指针
  2. 同样的消息发给不同类型的对象可能会使对象表现出不同的行为

方法在运行时绑定,也就是动态绑定才叫多态。重载不叫多态,重载时方法在编译期就已经绑定。 多态复用了接口。

策略模式

策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换。策略模式让算法独立于使用它的客户而独立变化。

当在业务上下文中有多个if-else来决定流程的话,就需要考虑使用策略模式了。它可以将if-else中各种平等的实现封装到单独的策略实现类中,这样使得系统更容易扩展。

/images/dp-strategy.png

策略模式很容易和模板方法模式结合使用。
策略模式的思想是用一个通用接口将不同的实现算法抽象出来,达到在运行时自由切换算法的目的,同时使得代码更容易应对变化。策略模式的典型应用场景比如说,一个应用需要保存文件,这个文件可以保存成很多种格式,每种格式的保存逻辑都不同,应用策略模式就可以让用户在运行随意选择哪种格式进行保存。

设计原则

  • 开闭原则
  • 封装变化

    找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。 在软件开发过程中,可能无法预知所有的变化,这就需要不断重构。

  • 针对接口编程,而不是针对实现编程

    如何分离变化?
    将变化抽象到一个接口,通过这个接口的不同实现来表现不同的变化。

  • 组合优先继承

    如何达到复用的目的?最直接的做法就是利用继承,然后出于这个目的的继承,往往会被过度使用。考虑两种情况下的继承:

    1. 和具体业务无关的继承 比如说工具方法等。这些方法和具体业务无关,业务的变更不会影响到这些方法的实现,因此使用继承来达到复用是可能的。
    2. 业务相关的继承 如果一个方法或属性与具体业务实现相关,那么就必须慎重考虑是否要被继承。因为业务逻辑很可能经常会发生变化,继承会导致所有的子类都会拥有这个特性,一旦子类的业务逻辑发生变化,将变得难以维护。而且,并不是所有的子类都要有这种特性。

    而通过组合,每个客户端对象通过组合的方式持有这些接口的引用,在运行时将这些变化(依赖)注入进来,这也是Spring中依赖注入的思想。注入的方式包括:构造方法注入,setter方法注入和接口注入。这样,我们达到了复用的目的—通过动态的组合这些变化对象来达到我们的需求。而且,更容易扩展和维护。

模板方法模式

定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

模板方法模式多用于高层框架设计。

设计原则

  • 好莱坞原则:Do not call us, we’ll call you.

装饰者模式

装饰者模式可以动态将职责附加到对象上,若要扩展功能,装饰者提供了比继承更具弹性的代替方案。

要点

  1. 装饰者和被装饰对象有相同的超类型。
  2. 可以用一个或多个装饰者包装一个对象。
  3. 装饰者可以在所委托被装饰者的行为之前或之后,加上自己的行为,以达到特定的目的。
  4. 对象可以在任何时候被装饰,所以可以在运行时动态的,不限量的用你喜欢的装饰者来装饰对象。
  5. 装饰模式中使用继承的关键是想达到装饰者和被装饰对象的类型匹配,而不是获得其行为。

设计原则

  • 组合优先继承
  • 对修改关闭,对扩展开放

适配器模式

将一个类的接口,转换成客户期望的另一个接口。

/images/dp-adapter.png

外观模式

外观模式提供了一个统一的接口,用来访问子系统中的一群接口。外观定义了一个高层接口,让子系统更容易使用。

/images/dp-facade.png

外观模式最大的作用,是对外提供一个简单的接口,同时避免客户代码与子系统之间有太多耦合。

实现一个外观,需要将子系统组合进外观中,然后将客户端的请求委托给子系统执行。

适配器模式将一个对象包装起来用以改变其接口;
装饰者模式将一个对象包装起来用以增加新的行为和责任;
外观模式将一群对象包装起来用以简化接口。
所有包装都通过组合的方式来实现。

命令模式

将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。

/images/dp-command.png

所谓参数化,指的是可以用不同的命令对象去配置客户的请求。客户发起的是同一个请求,但最好执行的是什么功能,要看配置的具体是什么命令对象。
命令模式的本质是将请求封装成命令对象。命令模式使用之前有一个命令组装的过程,调用顺序为:

  1. Client组装者创建命令执行者对象
  2. Client组装者创建命令对象,设置命令对象和命令执行者之间的关系
  3. Client组装者创建Invoker对象,将命令对象设置到Invoker对象中,让Invoker对象持有该命令对象
  4. Client组装者调用Invoker对象的方法,出发命令对象开始执行

状态模式

当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类。 

/images/dp-state.png

状态模式应用的主要场景在于,如果对同一个请求,一个对象表现出来的行为取决与它的状态,而且对象需要在任意两种状态之间进行自由切换。状态模式主要重构的目标是针对不同的状态充斥了大量的if/else判断,造成代码维护困难,严重违反开闭原则。
状态模式的典型应用场景包括:网上商城的订单流程, TCP协议的状态转换等等。
进行状态转换的地方可以是在Context中(可以定义一个nextState()方法驱动状态改变),也可以是在状态对象中(这时状态对象需要持有Context对象)。

状态模式与策略模式的区别

状态模式与策略模式十分相像,策略模式适用于这样的场景:

if (which == 1){
    applyThis();
} else if (which == 2) {
    applyThat();
} else if (which == 3) {
    ...
}

而状态模式适用于:

if (state == initial) {
    doSomething();
    state = processing;
} else if (state == processing) {
    doSomethingElse();
    state = done;
} else {
    ...
}

很明显对象面对客户的请求,都会触发自身内部状态的迁移。每当对象接受一个客户的请求,上述逻辑都要判断一下,对同一个请求,不同的对象状态下表现出来的行为也会不一样。
http://www.jdon.com/designpatterns/designpattern_State.htm

代理模式

代理模式为一个对象一个替身以**控制**对这个对象的访问。

/images/dp-proxy.png

代理模式使得客户对被代理对象的访问细节一无所知,代理对象控制了访问过程,代理模式的应用场景包括:

  1. 对被代理对象的访问进行拦截并控制访问(例如AOP机制)
  2. 控制资源加载,如让被代理对象在后台加载资源,而让代理对象显示提示信息,资源加载完毕后显示给用户
  3. 提供访问授权检查

代理模式与装饰者模式

装饰者模式和代理模式都会通过包装原有的对象,但装饰者模式会对原有的对象添加新的功能,而且这些功能可以任意组合;代理模式不会添加新的功能,原有的服务没有改变。

动态代理

从上面的类图可见,Proxy对象必须实现Subject这个业务接口,使得这个Proxy对象与具体的业务逻辑耦合起来了,对不同的业务接口,必须创建不同的Proxy对象。
Java提供了动态代理的支持,结构如下:

/images/dp-dynamic-proxy.png

java.lang.reflect.Proxy类实际上只是一个辅助类,真正的代理逻辑被委托到InvocationHandler的invoke方法中.
动态代理机制使得不用再手工编写代理类,只需要指定一个业务接口和一个委托对象,便能动态的获取代理对象,这个代理对象将来自客户端的调用分派到委托对象上执行,而委托对象可以根据需要添加必要的控制逻辑。

模式的最终目的是封装变化,不变的是接口,变化的是实现

Archlinux开机初始化基本步骤

| Comments

以下是对计算机开机加电后的自举流程的一个大致理解。

MBR

计算机开机后CPU处于16位实模式下,加上4位的偏移量,CPU能寻址的范围是20位,也就是1 MB的地址空间。CPU从存储器地址为0xFFFF0的位置读取一条跳转指令,这条指令将下一条指令地址指向系统BIOS的初始地址。对后BIOS程序执行机器自检POST(power on self test),检查系统基本的硬件设备,并检索系统中包含可启动代码的设备,所谓可启动的设备是指第一个扇区(sector)的最后两个字节包含0xAA55(boot签名),可以通过如下命令查看硬盘第一个扇区的信息:

$ sudo dd if=/dev/sda1 of=sector.bin bs=512 count=1
1+0 records in
1+0 records out
512 bytes (512 B) copied, 0.0245341 s, 20.9 kB/s
$ od -x sector.bin

启动设备的第一个扇区的512字节称为MBR(master boot record),MBR存放三部分内容:

  1. boot loader stage1程序 446字节   
  2. 硬盘分区表 64字节   
  3. 该扇区的有效标示(OxAA55) 2字节

BIOS检索到可启动的设备后,将这个设备的MBR区域的stage1程序加载到起始地址为0x7C00的一块内存区域,然后从这个地址执行stage1程序。接下来的启动过程分为几个阶段:

Bootloader

stage1加载执行stage1.5

stage1.5位于MBR区域后的30K范围内, 它包含文件系统相关的驱动代码, stage1.5可加载的文件系统包括:

  $ /bin/ls /boot/grub/*stage1_5
  /boot/grub/e2fs_stage1_5  /boot/grub/ffs_stage1_5      /boot/grub/jfs_stage1_5      /boot/grub/reiserfs_stage1_5  /boot/grub/vstafs_stage1_5
  /boot/grub/fat_stage1_5   /boot/grub/iso9660_stage1_5  /boot/grub/minix_stage1_5  /boot/grub/ufs2_stage1_5    /boot/grub/xfs_stage1_5
  $ file /boot/grub/reiserfs_stage1_5
  /boot/grub/reiserfs_stage1_5: GRand Unified Bootloader stage1_5 version 3.2, identifier 0x5, GRUB version 0.97, configuration file /boot/grub/stage2
  $ file /boot/grub/stage2
  /boot/grub/stage2: GRand Unified Bootloader stage2 version 3.2, identifier 0x0, GRUB version 0.97, configuration file /boot/grub/menu.lst

stage1.5加载stage2

在stage2阶段通过/boot/grub/menu.lst生成可启动的系统列表,并启动一个图形化的选择界面:

  timeout   7
  default   0
  color light-blue/black light-cyan/blue

  # (0) Arch Linux
  title  Arch Linux
  root   (hd0,7)
  kernel /boot/vmlinuz-linux root=/dev/sda8 ro
  initrd /boot/initramfs-linux.img

  # (1) Arch Linux
  title  Arch Linux Fallback
  root   (hd0,7)
  kernel /boot/vmlinuz-linux root=/dev/sda8 ro
  initrd /boot/initramfs-linux-fallback.img

  # (2) Windows
  title Microsoft Windows 7
  rootnoverify (hd0,1)
  makeactive
  chainloader +1

menu.lst指定了kernel和initrd镜像的路径。

解压kernel

当选择进入某个系统后,对应的kernel被载入内存并解压,控制权转移到kernel,kernel接下来初始化硬件,加载必要的驱动模块

加载initrd镜像

加载initrd(init ram filesystem,初始ram文件系统),在内存中展开位一个虚拟的文件系统,并执行其中的init脚本,通过以下命令可以揭开这个image文件,查看这个init脚本做了什么:

  $ cp /boot/initramfs-linux.img /tmp/initramfs/initramfs-linux.gz
  $ gzip -d initramfs-linux.gz 
  $ file initramfs-linux 
  initramfs-linux: ASCII cpio archive (SVR4 with no CRC)
  $ cpio -i <initramfs-linux 
  16076 blocks
  [calvin@arch-laptop /tmp/initramfs 01:11:09 ]
  $ ls
  total 7.9M
  lrwxrwxrwx 1 calvin calvin    7 May  7 01:11 bin -> usr/bin
  -rw-r--r-- 1 calvin calvin   70 May  7 01:11 config
  drwxr-xr-x 2 calvin calvin   40 May  7 01:11 dev
  drwxr-xr-x 3 calvin calvin  100 May  7 01:11 etc
  drwxr-xr-x 2 calvin calvin   60 May  7 01:11 hooks
  -rwxr-xr-x 1 calvin calvin 3.2K May  7 01:11 init
  -rw-r--r-- 1 calvin calvin 7.2K May  7 01:11 init_functions
  -rw-r--r-- 1 calvin calvin 7.9M May  7 01:10 initramfs-linux
  lrwxrwxrwx 1 calvin calvin    7 May  7 01:11 lib -> usr/lib
  drwxr-xr-x 2 calvin calvin   40 May  7 01:11 new_root
  drwxr-xr-x 2 calvin calvin   40 May  7 01:11 proc
  drwxr-xr-x 2 calvin calvin   40 May  7 01:11 run
  lrwxrwxrwx 1 calvin calvin    7 May  7 01:11 sbin -> usr/bin
  drwxr-xr-x 2 calvin calvin   40 May  7 01:11 sys
  drwxr-xr-x 2 calvin calvin   40 May  7 01:11 tmp
  drwxr-xr-x 4 calvin calvin  100 May  7 01:11 usr
  -rw-r--r-- 1 calvin calvin    5 May  7 01:11 VERSION

执行这个init脚本时,屏幕上会输出以下信息:

  :: Starting udevd...
  done.
  ... ... 

启动init进程

挂载真正的根文件系统,执行/sbin/init程序。
init程序首先读取/etc/inittab文件,这个过程参照here

Reference

Archlinux调教小记

| Comments

开机时设置屏幕亮度为最暗

sudo echo 'echo 0 > /sys/class/backlight/acpi_video0/brightness'>>/etc/rc.local

关机或者重启时最后屏幕变黑,但是机器一直关不掉,只有强行按电源键关闭

原因在于我试图在开机时关掉Navia显卡https://wiki.archlinux.org/index.php/Dell_XPS_15#System_Settings

rc.conf中
MODULES=(acpi_call)
rc.local中
echo '\_SB.PCI0.PEG0.PEGP._OFF' > /proc/acpi/call

由此会造成这个问题,去掉开机加载acpi_call模块就行了,为什么会这样呢?不知道…

音量设置

#pacman -S alsa-utils alsa-lib
#alsactl store

ibus输入中文显示乱码

使用ibus输入中文,在gedit下没有问题,在terminal中或者gvim中输入显示乱码 google得知是glibc的问题,在/etc/locale.gen中去掉zh_CN.UTF-8的注释,再运行locale-gen即可

下载firefox后执行./firefox失败,

$ /opt/firefox/firefox
/opt/firefox/firefox: error while loading shared libraries: libstdc++.so.6: cannot open shared object file: No such file or directory
$ file /opt/firefox/firefox
/opt/firefox/firefox: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.9, stripped
$ ldd /opt/firefox/firefox
    linux-gate.so.1 =>  (0xf7753000)
    libpthread.so.0 => /usr/lib32/libpthread.so.0 (0xf771d000)
    libdl.so.2 => /usr/lib32/libdl.so.2 (0xf7718000)
    libstdc++.so.6 => not found
    libm.so.6 => /usr/lib32/libm.so.6 (0xf76ea000)
    libgcc_s.so.1 => not found
    libc.so.6 => /usr/lib32/libc.so.6 (0xf7546000)
    /lib/ld-linux.so.2 (0xf7754000)

原因是firefox可执行文件是为32位平台编译的,在我的64位机器上的/lib等目录下缺少相应的64位库文件,下载ftp://ftp.mozilla.org/pub/firefox/64位firefox即可。

Microsoft wireless mobile mouse 3500纵向滚动过快

每当从windows重启进入arch时会发现微软无线鼠标3500滚轮纵向滚动太快,每次拔下无线接收器问题就能解决,然而有一个更好的办法,见here,here有一个patch可以解决这个问题。
编译安装后在/etc/rc.local中添加resetmsmice即可。

Disable PC Speaker Beep

https://wiki.archlinux.org/index.php/Disable_PC_Speaker_Beep

禁用IPv6

添加ipv6.disable=1你的启动加载器的内核行中。
另外可以通过sysctl禁用IPv6,添加下面的内容到 /etc/sysctl.conf:

# Disable IPv6
net.ipv6.conf.all.disable_ipv6 = 1

为什么要使用static内部类

| Comments

看一个例子:

public class OuterClass {
    class NonStaticInner {
    }
    static class StaticInner {
    }
    public void foo() {
        NonStaticInner nonStaticInner = new NonStaticInner();
        StaticInner staticInner = new StaticInner();
    }
}

编译后生成三个class文件:

$ tree
.
└── com
    └── java
        └── demo
            ├── OuterClass.class
            ├── OuterClass$NonStaticInner.class
            ├── OuterClass$StaticInner.class

使用javap反编译class文件:

$ javap -classpath . -private -c com.java.demo.OuterClass\$StaticInner
Compiled from "OuterClass.java"
class com.java.demo.OuterClass$StaticInner extends java.lang.Object{
com.java.demo.OuterClass$StaticInner();
  Code:
   0:   aload_0
   1:   invokespecial   #8; //Method java/lang/Object."<init>":()V
   4:   return
}

$ javap -classpath . -private -c com.java.demo.OuterClass\$NonStaticInner
Compiled from "OuterClass.java"
class com.java.demo.OuterClass$NonStaticInner extends java.lang.Object{
final com.java.demo.OuterClass this$0;

com.java.demo.OuterClass$NonStaticInner(com.java.demo.OuterClass);
  Code:
   0:   aload_0
   1:   aload_1
   2:   putfield    #10; //Field this$0:Lcom/java/demo/OuterClass;
   5:   aload_0
   6:   invokespecial   #12; //Method java/lang/Object."<init>":()V
   9:   return
}

从反编译的结果可以看出static内部类和non-static内部类的区别:
对non-static内部类来说,它会持有一个外部类的引用.编译器会生成一个构造方法,并传入外部类的引用,像这样:

class NonStaticInner {
    private final OuterClass this$0;

    OuterClass$NonStaticInner(OuterClass outer) {
        this$0 = outer;
    }
}

这样的话,每生成一个内部类,都会增加一个对外部类的引用,从而阻止GC来回收外部类。在Android中,如果这个外部类是一个activity类,从而会间接引用更多的图片等资源,不小心的话可能会造成内存泄漏。
而对static的内部类则不会持有所在外部类的引用。
所以,从GC的角度考虑,应该尽可能使用static的内部类.

Reference: