食用淡水鱼

食用淡水鱼 查看完整档案

杭州编辑浙江工业大学  |  英语 编辑滴滴出行  |  Android研发工程师 编辑 ffish.site 编辑
编辑

编程全靠自学

个人动态

食用淡水鱼 回答了问题 · 2016-09-09

Java中,怎么将当前时间的milliSeconds转换为hour级别的milliSeconds?

Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(1473406568);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
System.out.println(calendar.getTimeInMillis());

关注 5 回答 5

食用淡水鱼 关注了问题 · 2016-09-09

Java中,怎么将当前时间的milliSeconds转换为hour级别的milliSeconds?

场景:
得到一个时间戳A:1473406568
对应的日期是B:2016/9/9 15:36:8
现在需要的是时间戳C:2016/9/9 15:00:00 对应的毫秒。

现在有大量的毫秒A数据,需要直接转为C中值,请问应该怎么处理?

关注 5 回答 5

食用淡水鱼 关注了问题 · 2016-08-19

解决Android Studio 2.2 beta2 更新后强制将 Gradle 版本升级为 2.14.1,有没有遇到同样情况的?

今天早上更新了 Android Studio 2.2 beta2 版本,结果过不了编译了,提示如下图

clipboard.png

点击 Fix 就自动将 gradle 版本改为 2.14.1,Android plugin 改为 2.1.3 了。

我的 gradle 版本原来是 2.10,Android plugin 是 2.1.0,今天更新后就无法使用 Android plugin 是 2.1.0 了,gradle 版本倒是可以换回 2.1.0。

因为项目打包需求,还是希望使用 2.10 和 2.1.0 的配置,有没有遇到同样情况的同学,麻烦给点提示,多谢!

关注 3 回答 3

食用淡水鱼 回答了问题 · 2016-08-19

解决Android Studio 2.2 beta2 更新后强制将 Gradle 版本升级为 2.14.1,有没有遇到同样情况的?

这次版本更新,Studio做了件流氓的事——如果你的gradle目录是在studio目录下,更新后studio会把你的2.10 gradle强制删除,并用2.14.1给你替换,你需要重新下一个配置进去

如果你的gradle目录没有被删,那改下设置里面的gradle目录指向就可以了

关注 3 回答 3

食用淡水鱼 回答了问题 · 2016-05-10

解决gradle的环境变量该怎么配?

use default gradle wrapper
正确的命令应该是gradlew assembleDebug

关注 6 回答 5

食用淡水鱼 回答了问题 · 2016-03-13

解决Java中的字符串相加,内存怎么分配?

Java的运行时数据区中,有一个方法区(Method Area)和堆(Heap)。

对于那些在编译时就能确定的字面量都会存放在运行时常量池中,而常量池是方法区的一部分

Compiled from "Main.java"
public class ffish.top.Main {
  public ffish.top.Main();
    Code:
       0: aload_0       
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return        

  public static void main(java.lang.String[]);
    Code:
       0: ldc           #2                  // String hello 
       2: astore_1      
       3: new           #3                  // class java/lang/StringBuilder
       6: dup           
       7: invokespecial #4                  // Method java/lang/StringBuilder."<init>":()V
      10: aload_1       
      11: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      14: ldc           #6                  // String world
      16: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      19: invokevirtual #7                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      22: astore_2      
      23: ldc           #8                  // String hello world
      25: astore_3      
      26: ldc           #8                  // String hello world
      28: astore        4
      30: getstatic     #9                  // Field java/lang/System.out:Ljava/io/PrintStream;
      33: aload_2       
      34: aload         4
      36: if_acmpne     43
      39: iconst_1      
      40: goto          44
      43: iconst_0      
      44: invokevirtual #10                 // Method java/io/PrintStream.println:(Z)V
      47: getstatic     #9                  // Field java/lang/System.out:Ljava/io/PrintStream;
      50: aload_3       
      51: aload         4
      53: if_acmpne     60
      56: iconst_1      
      57: goto          61
      60: iconst_0      
      61: invokevirtual #10                 // Method java/io/PrintStream.println:(Z)V
      64: return        
}

反编译这段代码。其中ldc指令为加载一个常量到操作数栈。
02: astore_1 25: astore_3 28: astore 4分别对应a c d三个变量,它们都是通过ldc指令加载进来的,所以c和d指向同一块内存

22: astore_2 也就是变量b,是通过StringBuilder.toString()的方法生成的

    public String toString() {
        // Create a copy, don't share the array
        return new String(value, 0, count);
    }

通过查看StringBuildertoString()的方法,可以看到这里是通过new关键字来生成一个String对象,所以b指向的应该是堆中的字符对象。至于这个堆中的字符串对象和常量池中的字面量的关系,暂时还不太清楚。可能是通过clone的方式,在堆中新生成了一个字符串对象实现的。

Tip: 代码中的字符串拼接符号 + ,会被编译器重载为StringBuilderappend()方法以提高性能

关注 6 回答 4

食用淡水鱼 发布了文章 · 2016-03-03

C程序运行堆栈分析

最近在上孟宁老师的《Linux内核分析》,本文是该课程的实验作业,通过分析汇编代码来理解C程序在计算机中是如何工作的。分析的实验代码如下:
code

右边为通过gcc -S main.c -o main.s -m32命令转成的x86汇编代码,下文分析以右边代码为准
C代码

    int g(int x) {
        return x + 31;
    }
    
    int f(int x) {
        return g(x);
    }

    int main(void) {
        return f(52) + 33;
    }

x86汇编代码

    g:
        pushl %ebp
        movl  %esp, %ebp
        movl  8(%ebp), %eax
        addl  $31, %eax
        popl  %ebp
        ret
    f:
        pushl %ebp
        movl  %esp, %ebp
        subl  $4, %esp
        movl  8(%ebp), %eax
        movl  %eax, (%esp)
        call  g
        leave
        ret
    main:
        pushl %ebp
        movl  %esp, %ebp
        subl  $4, %esp
        movl  $52, (%esp)
        call  f
        addl  $33, %eax
        leave
        ret

由于C程序的入口为main函数,所以这段代码的起始点为第17行,eip(Extended Instruction Pointer, 指令寄存器)指向第18行(eip指向下一条指令)。程序在启动时,系统会为程序分配一个堆栈空间,此时程序的堆栈为空,ebp(Extended Base Pointer, 栈基指针寄存器)和esp(Extended Stack Pointer, 栈指针寄存器)都指向栈底。这里使用的内存堆栈模型为更常见的由下至上,而非课程视频中由上之下的结构。同时需要注意的是,右边的数字并非内存的实际地址,这里只是将内存做了简单的编号。
empty.png

18 pushl %ebp,将ebp寄存器中的值压栈,同时esp向上移一个单位
18.png

19 movl %esp, %ebp 将esp中的至赋值给ebp。此时,esp和ebp都指向1
19.png

20 subl $4, %esp 这条指令的直接作用是将esp中的值减去4,然后把结果存回esp中。这里需要说明两点:

  1. 这里的4指的是4个字节,也就是内存中真实地址移动4个单位(相当于本文模型中的1个单位)

  2. 因为栈是向低地址扩展的数据结构。对应本文内存模型就是,1的地址比0要小4个单位,2的地址比1要小4个单位,以此类推。这也是为什么这里用了减法指令

所以这条指令的执行结果就是将esp指向2
20.png

21 movl $52, (%esp) 这条指令的含义是将52这个数传入esp指向的内存地址中,也就是内存2
21.png

22 call f call是一个宏指令,其对应的两个指令为pushl %eipmovl f, %eip。上面说过eip的指代表下一条指令的位置,这里也就是第23行代码(记作EIP23)。pushl %eip就是将EIP23压栈,然后通过movl f, %eip将f函数的地址(EIP8)传入eip,使得下一条指令从f函数开始,从而实现C函数的调用。
22.png

9 pushl %ebp 将ebp的值入栈,也就是将EBP 1放入内存4中,同时esp上移一个单位
9.png

10 movl %esp, %ebp 将esp的值传入ebp中,此时esp 和 ebp同时指向内存4
10.png

11 subl $4, %esp 将esp上移一个单位,指向内存5
11.png

12 movl 8(%ebp), %eax8(%ebp) = (8 + %ebp) 也就是ebp指针下移两个单位,指向内存2,然后将内存2中的值(也就是52)传入eax(Extended Accumulator X,累加寄存器)。这条指令执行完后堆栈中并无变化,只是将52这个数传给了eax

13 movl %eax, (%esp) 将%eax中的数值传入%esp指向的内存位置(内存5)
13.png

14 call g 同样的,call相当于pushl %eipmovl g, %eip,此时eip指向第15条指令(记作EIP15)
14.png

2 pushl %ebp 将ebp的值入栈
2.png

3 movl %esp, %ebp 将esp的值传入ebp,执行后ebp和esp都指向内存7
3.png

4 movl 8(%ebp), %eax 将内存5中的数据(也就是52)传入eax。此时堆栈不变化
5 addl $31, %eax 将eax中的数据加上31,并把结果存入eax,所以此时eax中的值为83(52+31)
6 popl %ebp 将栈顶的数据弹出,并传入ebp,所以执行后ebp指向内存4。同时esp下移一个单位,指向内存6
6.png
7 ret ret也是一个宏指令,实际执行的效果为popl %eip,就是将栈顶的数据传入eip,同时esp下移一个单位,此时eip指向第15行指令
7.png
15 leave leave指令对应movl %ebp, %esppopl %ebp,先将ebp的值传入esp,执行后ebp和esp都指向内存4,然后将内存4的数据弹出并传入ebp中。所以执行leave执行后ebp指向内存1,esp指向内存3
15.png
16 ret 也就是popl %eip,执行后eip指向第23行代码,esp指向内存2
16.png
23 addl $33, %eax 将33累加到eax中,结果为116(83+33)
24 leavemovl %ebp, %esppopl %ebp,执行后ebp和esp均指向内存0。至此,改程序的堆栈又重新变为空栈
empty
25 ret 该程序执行结束,通过popl %eip将eip指向上个程序的指令

总结:通过分析可以看出,C语言其实是对汇编语言做了一层抽象,以方便程序员编写和阅读代码。计算机在执行程序时,也只能按部就班的逐条执行,这中间其实多了很多看似繁琐的过程。比如每次进入一个函数,都要先保存ebp指针。同时系统分配给每个程序的栈空间是有限的,如果调用的函数过多,则会导致栈溢出,引发程序异常。

查看原文

赞 1 收藏 5 评论 0

食用淡水鱼 回答了问题 · 2015-11-25

gradle中compile,provided,compile files,compile project有何区别?

1,3,4本质上是一样的,区别在于:

1 是从repository(默认是jCenter())里下载一个依赖包进行编译并打包
3 是从本地的libs目录下寻找picasso-2.4.0.jar这个文件进行编译并打包。类似的命令有`compile fileTree(dir: 'libs', include: '*.jar')`——将libs目录下所有jar文件进行编译并打包。
4 是将另一个module(等同eclipse中的library)进行编译并打包

至于provided,是提供给那些只编译不打包场景的命令。就是,我在编译的时候对某一个jar文件有依赖,但是最终打包apk文件时,我不想把这个jar文件放进去,可以用这个命令。目前我知道的就一个插件化的场景需要用到这个命令,如果搞不明白,可以先不管。

关注 6 回答 4

食用淡水鱼 回答了问题 · 2015-11-25

Android studio新建project还是module,为何文件那么大?

如果你进项目的目录下查看,会发现大文件都在app/build目录下,这个目录主要存放编译产生的.class, .dex,以及最后打包产生的.apk等文件。

你完全可以把这个目录删掉,然后再看看你项目的大小。但是每次编译后,这个目录仍然会产生。

关注 3 回答 2

食用淡水鱼 赞了问题 · 2015-09-29

希望增加踩人给出理由或者说法

@SFPM SF是一个技术问答的社区,社区的成员水平有所不同.大家回答问题 也是根据自己目前的水平 进行回答. 所以在回答的过程中难免出错误,错了 你可以进行踩的操作,但是 也请你在点一下的同时说出你的理由好嘛..如果是单纯的踩操作 没有任何的理由 任何的说明 我觉得是对回答者的不尊重.

关注 6 回答 5

认证与成就

  • 获得 108 次点赞
  • 获得 27 枚徽章 获得 2 枚金徽章, 获得 12 枚银徽章, 获得 13 枚铜徽章

擅长技能
编辑

开源项目 & 著作
编辑

(゚∀゚ )
暂时没有

注册于 2013-07-08
个人主页被 2.6k 人浏览