Java8的lambda(3)

lambda和匿名类

对普通的开发者来说,可以理解为lambda是匿名类的语法糖;

但是实际上不是的,lambda是通过invokedynamic来实现的,而匿名类是通过正常的产生一个类来执行的。

两者是完全不一样的。

例如:

package functional;

/*
 * Implementing the interface by creating an
 * anonymous inner class versus using 
 * lambda expression.
 */
public class SimpleFunInterfaceTest {
    public static void main(String[] args) {

        // anonymous inner class
        carryOutWork(new SimpleFuncInterface() {
            @Override
            public void doWork() {
                System.out.println("Do work in SimpleFun impl...");
            }
        });

        // lambda
        carryOutWork(() -> sleep());
    }

    private static Object sleep() {
        try {
            Thread.sleep(1500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        return null;
    }

    public static void carryOutWork(SimpleFuncInterface sfi) {
        sfi.doWork();
    }
}

上面这段代码一个是通过匿名类来实现,一个是通过lambda来实现,可以明显的看出lambda更加简洁;

但是两个的实现方法不一样;

匿名类会在class文件中生成一个名为SimpleFunInterfaceTest$1.class的匿名类;

而lambda会生成Bootstrap方法和lambda$0方法,然后通过invokedynamic将两者关联起来,在实际的运行过程中会生成临时的实例来调用lambda函数。

Java8的lambda(2)

在开始之前,我先普及一下什么是Single Abstract Method interfaces (SAM
Interfaces).简单的说就是只用一个函数的interface。

例如Runnable,Comparable等;

例如下面一个:

package functional;

@FunctionalInterface
public interface SimpleFuncInterface {
  public void doWork();

  public String toString();
}

lambda就是通过MethodHandle来实现的。

那么在运行的过程中,它是什么样子的呢?

看看下面的例子:

package functional;

public class SimpleFunInterfaceTest2 {
    public static void main(String[] args) {
        // lambda
        carryOutWork(() -> sleep());
        carryOutWork(() -> sleep());
    }

    private static Object sleep() {
        try {
            Thread.sleep(1500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

         return null;
    }

    public static void carryOutWork(SimpleFuncInterface sfi) {
        sfi.doWork();
    }
}

在Thread.sleep那一行打个断点,这时的堆栈是:

“main” #1 prio=5 os_prio=0 tid=0x00e9cc00 nid=0x5e8 at breakpoint[0x001cf000]
java.lang.Thread.State: RUNNABLE
at functional.SimpleFunInterfaceTest.sleep(SimpleFunInterfaceTest.java:24)
at functional.SimpleFunInterfaceTest.lambda$0(SimpleFunInterfaceTest.java:20)
at functional.SimpleFunInterfaceTest$$Lambda$1/7599890.doWork(Unknown Source)
at
functional.SimpleFunInterfaceTest.carryOutWork(SimpleFunInterfaceTest.java:34)
at functional.SimpleFunInterfaceTest.main(SimpleFunInterfaceTest.java:20)

JVM会生成一个7599890实例来实行对应的lambda,如果你再起一个同样的程序,这时的堆栈和第一次是一样的;

如果在第二次嗲用carryOutWork的Thread.sleep上停住的话,这是的堆栈就不一样了,如下图:

这是为什么呢?这是因为两次调用carryOutWork方法,都会在class文件中生成两个lambda方法,所以这里的实例对象不一样;

一个是25332239,一个是7599890.这里的数字应该是根据什么lambda的位置和实现的interface名字生成的,同一个lambda在不同的JVM实例中生成的数字是一样的,

这里应该就是CallSite了。

但是你如果想只生成一个lambda的话,应该怎么办呢?

SimpleFuncInterface sfi = () -> sleep();
carryOutWork(sfi);
carryOutWork(sfi);

Java8的lambda(1)

Java为什么要引入lambda,一句话就是为了在多核时代提高并发,因为lambda是一个闭包,和外界是独立的,多个lambda可以同时在多核上运行。

lambda的两种简单的使用方法

package defaults;

import java.util.ArrayList;
import java.util.List;

public class Clazz2 {

    private void test(String a) {
        //
    } 

    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("one");
        list.add("two");
        list.add("three");

        //method reference
        list.forEach(System.out::println);
        Clazz2 tmp = new Clazz2();
        list.forEach(tmp::test);

        //lambda
        list.forEach((s) -> System.out.println("lambda0 " + s));
        list.forEach((s) -> System.out.println("lambda1 " + s));
    }
}

第一种方法是用method reference。使用这种方法生成的class文件中没有lambda$0这样的函数,但是会有Bootstrap方法;

这里使用了两种类型的method reference,一种是静态方法的,一种是实例方法的,区别只是前者会生成getstatic
字节码,后者会生成如下的一段代码来获得reference:

49  new defaults.Clazz2 [1]
52  dup
53  invokespecial defaults.Clazz2() [49]
56  astore_2 [tmp]
57  aload_1 [list]
58  aload_2 [tmp]

第二种方法是使用lambda的标准定义方法,这样会在生成的class文件中生成lambda$0,和lambda$1的函数,然后通过Bootstrap方法将调用的地方和lambda函数关联起来。

其实lambda的实现就是依靠invokedynamic命令来实现的,简单的说就是在遇到lambda的时候,生成一个invokedynamic字节码,JVM在执行到这条语句的时候,就会查找对应的Bootstrapmethod表,从中找到对应的Bootstrapmethod,其实这里叫做CallSite,然后在CallSite中找到对应的MethodHandle,通过MethodHandle来包装lambda方法,并传入想要的参数,这是就结束了;下次如果执行到同一个CallSite的时候,直接返回对应的MethodHandle就可以了,不需要再次查表。

至于为什么使用invokedynamic来实现lambda,这是一个很长的话题,感兴趣的人可以google一下。

http://blog.sanaulla.info/2013/03/21/introduction-to-functional-interfaces-
a-concept-recreated-in-java-8/

https://docs.oracle.com/javase/8/docs/api/java/lang/FunctionalInterface.html

http://java.dzone.com/articles/dark-side-lambda-expressions

http://blog.csdn.net/zxhoo/article/details/38387141

Java8中的default方法

网上有很多的介绍,我就不详细的介绍了。

比如:

http://blog.csdn.net/wwwsssaaaddd/article/details/24213525

它的本意就是为了不破坏现在interface的结构,但是又能给interface中追加新的方法。

这里只说一下一个interface可以有多个default方法,default方法可以重载.

例如:

package defaults;

public interface A {
    default void foo(){ 
       System.out.println("Calling A.foo()");
    }

    default void foo(String a){
        System.out.println("Calling A.foo(Strings)");
     }

    default void foo2(){
        System.out.println("Calling A.foo2()");
     }
}

如果default方法有冲突,可以使用A.super.foo();来解决,其实就是invokespecial

Android的launchMode

Android的activity的launchMode有四种;

1. standard

这样的activity在task中是标准的先进后出;例如task中的情况是:A–>B–>C 用户按了back, task就变成了A–>B

2. singleTop

如何activity在task的顶部,那么不需要重新创建新的activity,直接使用它就可以了;

例如task中的情况是:A–>B–>C,这时系统来了一个Intent,要求启动C(是singleTop),
这时C已经在栈顶了,不需要创建新的C,直接使用就可以了。

但是如果系统需要的是B(是singleTop),这时就要创建新的B,因为B没有在栈顶;

3. singleTask

在一个task中保证某个activity的唯一性;

例如task中的情况是:A–>B–>C 这时系统来了一个Intent,要求启动C(是singleTask), 不需要创建新的C,直接使用就可以了。

再例如task中的情况是:A–>B–>C
这时系统来了一个Intent,要求启动B(是singleTask),系统会将B之上的所以activity出栈,直接使用B,这时栈变成了:A–>B

再例如task1中的情况是:A–>B–>C;task2中的情况是D–>E;这时系统来了一个Intent,要求启动B(是singleTask),系统会将task1的栈和task2合并变成:

D–>E–>A–>B

4. singleInstance

和singleTask一样,只是在包含singleInstance的task中只能由singleInstance的activity;

Linux下的real UID和effective UID

real UID是标记谁调用了该可执行文件;effective UID表示该可执行程序所具有的权限的用户;

默认情况下real UID和effective UID一样,但是当使用了setUID之后,两者有可能不一样。

例如passwd程序,当你执行这个程序的时候,它的real UID就是调用passwd的用户,这个用户可能是root,也可能是任何普通用户;

但是它的effective UID是root,只有这样passwd程序才能修改/etc/passwd文件。

http://www.lst.de/~okir/blackhats/node23.html

在Linux下,如何将程序crash的堆栈对应到对应的行

假设你的堆栈其中的一条输出如下:

libYourLibName.so(_ZN28YourMethodNameEv+0x2c)[0x2aac22cda14c]

lib名字(出错的方法名+偏移量)[运行时的具体地址]; 这里的方法名系统给追加了一些自己的前缀和后缀。

1。使用make debug,产生带有debug符号的lib文件;

2。使用nm找出_ZN28YourMethodNameEv对应的虚拟内存的地址;假设是0x800

3。计算出错的位置:0x800+0x2c = 0x82c;

4。使用addr2line -e libYourLibName.so运行,然后再输入0x82c,addr2line会输出对于得文件和行号

Ubuntu下面的一些操作

安装JDK:
sudo apt-get update

sudo apt-get install default-jdk

在Ubuntu 12.04之前会安装OpenJDK 6,在12.10之后会安装OpenJDK 7。

https://www.digitalocean.com/community/tutorials/how-to-install-java-on-
ubuntu-with-apt-get

在文件管理器中复制文件的全路径:
ctrl+L; ctrl+c

安装unzip工具:

apt-get install unzip

修改root密码:

sudo password root

在安装VMware tools到虚拟机中时,会弹出对话框,让你的输入root的密码,注意这里的root密码是你运行Player的系统的root密码。

在Ubuntu上安装ADT的遭遇

现在使用的Linux机器不能直接运行ADT中自带的adb,原因是因为platform-tools是20之后的adb需要glibc的版本必须大于2.7,但是在

使用的系统里面使用的是2.6。所以只能安装虚拟机来试试运行 adb了。

可以通过下面的命令来获得glibc的版本:

* On systems with rpm packet-manager like RedHat, Dedora, Mandriva, SuSe, ...
      o rpm -q glibc
* On systems with dpkg packet-manager like Debian, ubuntu, ...
      o dpkg -l libc6
* Or you can run on any system
      o /lib/libc<TAB><ENTER>

http://freeadhocudf.org/documentation_english/dok_eng_howto_glibc.html

所以下使用ADT的话,有两种方式,升级系统的glibc,或者使用虚拟机。

我尝试了安装新版本的glibc,但是提示我的bash和其他的工具太老,我就没有载尝试,直接放弃–> fail

然后就是使用虚拟机了;

下载VMware Player和VirtualBox;

再下载了64位的Ubuntu,谁知道adb不支持64位的系统,你必须将architecture设置成i386,然后下载需要的lib,这样很是麻烦 – >
fail

再下载32位的Ubuntu,但是应为下载的ADT是64位的,所以eclipse不能在32位下运行,只能再下载32位的ADT,然后安装。

一切都处理好后,ADT可以正常启动,adb也可以运行,但是android的模拟器在VMware中根本启动不起来,直接说少openGL-es lib –〉
fail

换成Windows 7的虚拟机,也是运行不起来模拟器,直接导致Windows hang up –〉 fail

所以最终的结果在Linux的虚拟机中跑不起来android的模拟器。