夜光:Java成神之路(四)擅长的语言

夜光序言:

 

女孩问:“你喜欢我吗?”
“不喜欢”男孩不假思索的回答
“哦”女孩失落地低下头,眼泪快要掉下来
“傻瓜,我不喜欢你,我爱你”男孩宠溺的摸摸女孩的头

 

 

 

 

 

 

 
 
正文:
 
                                              以道御术 / 以术识道

 



 

6. throw 和 throws 的区别

 

throw:
 
1)throw 语句用在方法体内,表示抛出异常,由方法体内的语句处理。
 
2)throw 是具体向外抛出异常的动作, 所以它抛出的是一个异常实例,执行 throw 一定是抛出了某种异常。
 
 
throws
 
1)throws 语句是用在方法声明后面,表示如果抛出异常,由该方法的调用者来进行异常的处理。
 
2)throws 主要是声明这个方法会抛出某种类型的异常,让它的使用者要知道需要捕获的异常的类型。
 
3) throws 表示出现异常的一种可能性,并不一定会发生这种异常

 

 

 


 

7. final、finally、finalize 的区别?

 

1)final :用于声明属性,方法和类,分别表示属性不可变,方法不可覆盖,被其修饰的类不可继承。
 
2)finally 异常处理语句结构的一部分,表示总是执行。
 
3)finalize Object 类的一个方法,在垃圾回收器执行的时候会调用被回收对象的此方法,可以覆盖此方法提供垃圾收集时的其他资源回收,例如关闭文件等。
 
该方法更像是一个对象生命周期的临终方法,当该方法 被系统调用则代表该对象即将“死亡”
 
 
但是需要注意的是,我们主动行为上去调用该方法并不会导致该对 象“死亡”,这是一个被动的方法(其实就是回调方法),不需要我们调用
 
 
 
 

 

 

五、JavaSE 常用 API

 

1. Math.round(11.5)等于多少?Math.round(- 11.5) 又等于多少?

 
Math.round(11.5)的返回值是 12,Math.round(-11.5)的返回值是-11。
 
四舍五入的原理是在参数上加 0.5然后进行取整。
 
 

 

 

2. switch 是否能作用在 byte 上,是否能作用在 long 上,是否能作用在 String上?

 
Java5 以前 switch(expr)中,expr 只能是 byte、short、char、int。从 Java 5 开始,Java 中引入了枚举类型,
 
 
expr 也可以是 enum 类型。
 
 
从 Java 7 开始,expr 还可以是字符串(String),但是长整型(long)在目前所有的版本中都是不可以的
 
 

 

3. 数组有没有 length() 方法?String 有没有 length() 方法?

 
数组没有 length()方法,而是有 length 的属性。
 
String 有 length()方法。
 
JavaScript 中,获得字符串的长度是通过 length 属性得到的,这一点容易和 Java 混淆。
 
 

 


 

4. String 、StringBuilder 、StringBuffer 的区别?

 

Java 平台提供了两种类型的字符串:String 和 StringBuffer/StringBuilder,它们都可以储存和操作字符串,区别如下
 
 
1)String 是只读字符串,也就意味着 String 引用的字符串内容是不能被改变的。
 
初学者可能会有这样的误解:
 
1. String str = “abc”
2. str = “bcd”;
 
如上,字 符串 str 明明是可以改变的啊        其实不然,str 仅仅是一个引用对象,它指向一个字符串对象“abc”。
 
第二行代码的含义是让 str 重新指向了一个新的字符串“bcd”对象,而“abc”对象并没有任何改变,只不过该对象已 经成为一个不可及对象罢了。
 
2)StringBuffer/StringBuilder 表示的字符串对象可以直接进行修改。
 
 
 
3)StringBuilder 是 Java5 中引入的,它和 StringBuffer 的方法完全相同,区别在于它是在单线程环境下使用的,
 
因为它的所有方法都没有被 synchronized 修饰,因此它的效率理论上也比 StringBuffer 要高。

 

 

5. 什么情况下用“+”运算符进行字符串连接比调用 StringBuffer/StringBuilder    对象的 append 方法连接字符串性能更好?

 

字符串是 Java 程序中最常用的数据结构之一。
 
在 Java 中 String 类已经重载了"+"。
 
也就是说,字符串可以直接 使用"+"进行连接,如下面代码所示:
 
 
1.String s = "abc" + "ddd"; 
 
但这样做真的好吗?当然,这个问题不能简单地回答 yes or no。
 
要根据具体情况来定。
 
在 Java 中提供了一个StringBuilder 类(这个类只在 J2SE5 及以上版本提供,以前的版本使用 StringBuffer 类),这个类也可以起到"+"的 作用。
 
 

那么我们应该用哪个呢?

 
下面让我们先看看如下的代码:
 
1. package string;
2.
3. public class TestSimplePlus
4. {
5. public static void main(String[] args)
6. {
7. String s = "abc";
8. String ss = "ok" + s + "xyz" + 5;
9. System.out.println(ss);
10. }
11. }


上面的代码将会输出正确的结果。从表面上看,对字符串和整型使用"+"号并没有什么区别,但事实真的如此吗?

下面让我们来看看这段代码的本质。

我们首先使用反编译工具(如 jdk 带的 javap、或 jad)将 TestSimplePlus 反编译成 Java Byte Code,其中的奥秘就一目了然了。在本文将使用 jad 来反编译,命令如下:
jad -o -a -s d.java TestSimplePlus.class

 


反编译后的代码如下:


1. package string;
2.
3. import java.io.PrintStream;
4.
5. public class TestSimplePlus
6. {
7. public TestSimplePlus()
8. {
9. // 0 0:aload_0
10. // 1 1:invokespecial #8 <Method void Object()>
11. // 2 4:return
12. }
13.
14. public static void main(String args[])
15. {
16. String s = "abc";
17. // 0 0:ldc1 #16 <String "abc">
18. // 1 2:astore_1
19. String ss = (new StringBuilder("ok")).append(s).append("xyz").append(5).toString();
20. // 2 3:new #18 <Class StringBuilder>
21. // 3 6:dup
22. // 4 7:ldc1 #20 <String "ok">
23. // 5 9:invokespecial #22 <Method void StringBuilder(String)>
24. // 6 12:aload_1
25. // 7 13:invokevirtual #25 <Method StringBuilder StringBuilder.append(String)>
26. // 8 16:ldc1 #29 <String "xyz">
27. // 9 18:invokevirtual #25 <Method StringBuilder StringBuilder.append(String)>
28. // 10 21:iconst_5
29. // 11 22:invokevirtual #31 <Method StringBuilder StringBuilder.append(int)>
30. // 12 25:invokevirtual #34 <Method String StringBuilder.toString()>
31. // 13 28:astore_2
32. System.out.println(ss);
33. // 14 29:getstatic #38 <Field PrintStream System.out>
34. // 15 32:aload_2
35. // 16 33:invokevirtual #44 <Method void PrintStream.println(String)>
36. // 17 36:return
37. }
38. }


读者可能看到上面的 Java 字节码感到迷糊,不过大家不必担心。
 
 
本文的目的并不是讲解 Java Byte Code,因此,并不用了解具体的字节码的含义。
 
 
使用 jad 反编译的好处之一就是可以同时生成字节码和源代码。
 
这样可以进行对照研究。
 
从上面的代码很容易看 出,虽然在源程序中使用了"+",但在编译时仍然将"+"转换成 StringBuilder。因此,我们可以得出结论, Java 无论使用何种方式进行字符串连接,实际上都使用的是 StringBuilder
 
 
那么是不是可以根据这个结论推出使用"+"和 StringBuilder 的效果是一样的呢?
 
这个要从两个方面的解释。
 
如果 从运行结果来解释,那么"+"和 StringBuilder 是完全等效的。但如果从运行效率和资源消耗方面看,那它们将存在很 大的区别。
 
 
当然,如果连接字符串行表达式很简单(如上面的顺序结构),那么"+"和 StringBuilder 基本是一样的,但如果结构比较复杂,如使用循环来连接字符串,那么产生的 Java Byte Code 就会有很大的区别。
 
 
先让我们看看如下的代码:
1. package string;
2.
3. import java.util.*;
4.
5. public class TestComplexPlus
6. {
7. public static void main(String[] args)
8. {
9. String s = "";
10. Random rand = new Random();
11. for (int i = 0; i < 10; i++)
12. {
13. s = s + rand.nextInt(1000) + " ";
14. }
15. System.out.println(s);
16. }
17. }
 
 
上面的代码返编译后的 Java Byte Code 如下:
1. package string;
2.
3. import java.io.PrintStream;
4. import java.util.Random;
5.
6. public class TestComplexPlus
7. {
8.
9. public TestComplexPlus()
10. {
11. // 0 0:aload_0
12. // 1 1:invokespecial #8 <Method void Object()>
13. // 2 4:return
14. }
15.
16. public static void main(String args[])
17. {
18. String s = "";
19. // 0 0:ldc1 #16 <String "">
20. // 1 2:astore_1
21. Random rand = new Random();
22. // 2 3:new #18 <Class Random>
23. // 3 6:dup
24. // 4 7:invokespecial #20 <Method void Random()>
25. // 5 10:astore_2
26. for(int i = 0; i < 10; i++)
27. //* 6 11:iconst_0
28. //* 7 12:istore_3
29. //* 8 13:goto 49
30. s = (new StringBuilder(String.valueOf(s))).append(rand.nextInt(1000)).append(" ").t
oString();
31. // 9 16:new #21 <Class StringBuilder>
32. // 10 19:dup
33. // 11 20:aload_1
34. // 12 21:invokestatic #23 <Method String String.valueOf(Object)>
35. // 13 24:invokespecial #29 <Method void StringBuilder(String)>
36. // 14 27:aload_2
37. // 15 28:sipush 1000
38. // 16 31:invokevirtual #32 <Method int Random.nextInt(int)>
39. // 17 34:invokevirtual #36 <Method StringBuilder StringBuilder.append(int)>
40. // 18 37:ldc1 #40 <String " ">
41. // 19 39:invokevirtual #42 <Method StringBuilder StringBuilder.append(String)>
42. // 20 42:invokevirtual #45 <Method String StringBuilder.toString()>
43. // 21 45:astore_1
44.
45. // 22 46:iinc 3 1
46. // 23 49:iload_3
47. // 24 50:bipush 10
48. // 25 52:icmplt 16
49. System.out.println(s);
50. // 26 55:getstatic #49 <Field PrintStream System.out>
51. // 27 58:aload_1
52. // 28 59:invokevirtual #55 <Method void PrintStream.println(String)>
53. // 29 62:return
54. }
55. }

 

 

大家可以看到,虽然编译器将"+"转换成了 StringBuilder,但创建 StringBuilder 对象的位置却在 for 语句内部。
 
这就意味着每执行一次循环,就会创建一个 StringBuilder 对象(对于本例来说,是创建了 10 个 StringBuilder 对象),虽然 Java 有垃圾回收器,但这个回收器的工作时间是不定的。
 
 
如果不断产生这样的垃圾,那么仍然会占用大量的资源。解决这个问题的方法就是在程序中直接使用 StringBuilder 来连接字符串,代码如下:
1. package string;
2.
3. import java.util.*;
4.
5. public class TestStringBuilder
6. {
7. public static void main(String[] args)
8. {
9. String s = "";
10. Random rand = new Random();
11. StringBuilder result = new StringBuilder();
12. for (int i = 0; i < 10; i++)
13. {
14. result.append(rand.nextInt(1000));
15. result.append(" ");
16. }
17. System.out.println(result.toString());
18. }
19. }

 

上面代码反编译后的结果如下:

 

1. package string;
2.
3. import java.io.PrintStream;
4. import java.util.Random;
5.
6. public class TestStringBuilder
7. {
8.
9. public TestStringBuilder()
10. {
11. // 0 0:aload_0
12. // 1 1:invokespecial #8 <Method void Object()>
13. // 2 4:return
14. }
15.
16. public static void main(String args[])
17. {
18. String s = "";
19. // 0 0:ldc1 #16 <String "">
20. // 1 2:astore_1
21. Random rand = new Random();
22. // 2 3:new #18 <Class Random>
23. // 3 6:dup
24. // 4 7:invokespecial #20 <Method void Random()>
25. // 5 10:astore_2
26. StringBuilder result = new StringBuilder();
27. // 6 11:new #21 <Class StringBuilder>
28. // 7 14:dup
29. // 8 15:invokespecial #23 <Method void StringBuilder()>
30. // 9 18:astore_3
31. for(int i = 0; i < 10; i++)
32. //* 10 19:iconst_0
33. //* 11 20:istore 4
34. //* 12 22:goto 47
35. {
36. result.append(rand.nextInt(1000));
37. // 13 25:aload_3
38. // 14 26:aload_2
39. // 15 27:sipush 1000
40. // 16 30:invokevirtual #24 <Method int Random.nextInt(int)>
41. // 17 33:invokevirtual #28 <Method StringBuilder StringBuilder.append(int)>
42. // 18 36:pop
43. result.append(" ");
44. // 19 37:aload_3
45. // 20 38:ldc1 #32 <String " ">
46. // 21 40:invokevirtual #34 <Method StringBuilder StringBuilder.append(String)>
47. // 22 43:pop
48. }
49.
50. // 23 44:iinc 4 1
51. // 24 47:iload 4
52. // 25 49:bipush 10
53. // 26 51:icmplt 25
54. System.out.println(result.toString());
55. // 27 54:getstatic #37 <Field PrintStream System.out>
56. // 28 57:aload_3
57. // 29 58:invokevirtual #43 <Method String StringBuilder.toString()>
58. // 30 61:invokevirtual #47 <Method void PrintStream.println(String)>
59. // 31 64:return
60. }
61. }



 

从上面的反编译结果可以看出,创建 StringBuilder 的代码被放在了 for 语句外。
 
 
虽然这样处理在源程序中看起来复杂,但却换来了更高的效率,同时消耗的资源也更少了。
 
 

在使用 StringBuilder 时要注意,尽量不要"+"和 StringBuilder 混着用,否则会创建更多的 StringBuilder 对象,如下面代码所:

for (int i = 0; i < 10; i++)
{
result.append(rand.nextInt(1000));
result.append(" ");
}

 

改成如下形式:

for (int i = 0; i < 10; i++)
{
result.append(rand.nextInt(1000) + " ");
}

 

则反编译后的结果如下:

for(int i = 0; i < 10; i++)
//* 10 19:iconst_0
//* 11 20:istore 4
//* 12 22:goto 65
{
result.append((new StringBuilder(String.valueOf(rand.nextInt(1000)))).append(" ").toString());
// 13 25:aload_3
// 14 26:new #21 <Class StringBuilder>
// 15 29:dup
从上面的代码可以看出,Java 编译器将"+"编译成了 StringBuilder,这样 for 语句每循环一次,又创建了一个StringBuilder 对象。
 
 
如果将上面的代码在 JDK1.4 下编译,必须将 StringBuilder 改为 StringBuffer,而 JDK1.4 将"+"转换为 StringBuffer(因为 JDK1.4 并没有提供 StringBuilder 类)。
 
 

StringBuffer 和 StringBuilder 的功能基本一样,只是 StringBuffer 是线程安全的,而 StringBuilder 不是线程安全的。

 
 
因此,StringBuilder 的效率会更高。

 


 

6. 请说出下面程序的输出

1. class StringEqualTest {
2. public static void main(String[] args) {
3. String s1 = "Programming";
4. String s2 = new String("Programming");
5. String s3 = "Program";
6. String s4 = "ming";
7. String s5 = "Program" + "ming";
8. String s6 = s3 + s4;
9. System.out.println(s1 == s2); //false
10. System.out.println(s1 == s5); //true
11. System.out.println(s1 == s6); //false
12. System.out.println(s1 == s6.intern()); //true
13. System.out.println(s2 == s2.intern()); //false
14. }
15. }

 

 

 

补充:解答上面的需要知道如下两个知识点:

 
 
1. String 对象的 intern()方法会得到字符串对象在常量池中对应的版本的引用(如果常量池中有一个字符串与String 对象的 equals 结果是 true)
 
如果常量池中没有对应的字符串,则该字符串将被添加到常量池中,然后返 回常量池中字符串的引用
 
 
2. 字符串的+操作其本质是创建了 StringBuilder 对象进行 append 操作
 
 
然后将拼接后的 StringBuilder 对象用 toString 方法处理成 String 对象
 
 
 

这一点可以用 javap -c StringEqualTest.class 命令获得 class 文件对应的 JVM 字节码指令就可以看出来。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
JAD本身是一个命令行工具,没有图形界面,我之前上传的Cavaj Java Decompiler就是以jad为内核,加了一个图形界面…… 反编译工具jad简单用法   以下假设jad.exe在c:\java目录下   一、基本用法   Usage: jad [option(s)]   直接输入类文件名,且支持通配符,如下所示。   c:\java\>jad example1.class   c:\java\>jad *.class   结果是将example1.class反编译为example1.jad。将example1.jad改为example1.java即得源文件。   二、Option -o   不提示,覆盖源文件   三、Option -s   c:\java\>jad -sjava example1.class   反编译结果以.java为扩展名。   、Option -p   将反编译结果输出到屏幕   c:\java\>jad -p example1.class   将反编译结果重定向到文件   c:\java\>jad -p example1.class>example1.java   五、Option -d   指定反编译的输出文件目录   c:\java\>jad -o -dtest -sjava *.class   命令行选择的列表   -a - 用JVM字节格式来注解输出   -af - 同 -a,但是注解的时候用全名称   -clear - 清除所有的前缀   -b - 输出多于的括号 (e.g., if(a) { b(); }, default: no)   -d   - 指定输出文件的文件目录   -dead -试图反编译代码的dead 部分(default: no)   -disass - 不用用字节码的方式反编译 (no JAVA source generated)   -f - 输出整个的名字,无论是类还是方法   -ff -输出类的成员在方法之前 (default: after methods)   -i - 输出所有的变量的缺省的最初值   -l - 将strings分割成指定数目的块的字符 (default: no)   -lnc - 将输出文件用行号来注解 (default: no)   -nl - 分割strings用新行字符 newline character (default: no)   -nodos -不要去检查class文件是否以dos方式写 (CR before NL, default: check)   -nocast - 不要生成辅助文件   -nocode -不要生成方法的源代码   -noconv - 不要转换java的定义符 (default: do)   -noctor - 不允许空的构造器存在   -noinner -关掉对内部类的支持 (default: turn on)   -nolvt - 忽略局部变量的表信息   -nonlb - 不要输出一个新行在打开一个括号之前 (default: do)   -o - 无需确认直接覆盖输出 (default: no)   -p - 发送反编译代码到标准输出 STDOUT (e.g., for piping) 很多人困扰,反编译之后中文显示乱码问题,其实显示的不是乱码,是unicode字符。 -8 - 将Unicode字符转换为ANSI字符串,如果输出字符串是中文的话一定要加上这个参数才能正确显示。 最常用的反编译指令如下所示: Jad –d D:\javasource –s .java -8 javatest.class 这条指令将当前目录下的javatest.class反编译为javatest.java并保存在D:\javasource目录里,其中的提示输出为中文,而不是Unicode代码。
4S店客户管理小程序-毕业设计,基于微信小程序+SSM+MySql开发,源码+数据库+论文答辩+毕业论文+视频演示 社会的发展和科学技术的进步,互联网技术越来越受欢迎。手机也逐渐受到广大人民群众的喜爱,也逐渐进入了每个用户的使用。手机具有便利性,速度快,效率高,成本低等优点。 因此,构建符合自己要求的操作系统是非常有意义的。 本文从管理员、用户的功能要求出发,4S店客户管理系统中的功能模块主要是实现管理员服务端;首页、个人中心、用户管理、门店管理、车展管理、汽车品牌管理、新闻头条管理、预约试驾管理、我的收藏管理、系统管理,用户客户端:首页、车展、新闻头条、我的。门店客户端:首页、车展、新闻头条、我的经过认真细致的研究,精心准备和规划,最后测试成功,系统可以正常使用。分析功能调整与4S店客户管理系统实现的实际需求相结合,讨论了微信开发者技术与后台结合java语言和MySQL数据库开发4S店客户管理系统的使用。 关键字:4S店客户管理系统小程序 微信开发者 Java技术 MySQL数据库 软件的功能: 1、开发实现4S店客户管理系统的整个系统程序; 2、管理员服务端;首页、个人中心、用户管理、门店管理、车展管理、汽车品牌管理、新闻头条管理、预约试驾管理、我的收藏管理、系统管理等。 3、用户客户端:首页、车展、新闻头条、我的 4、门店客户端:首页、车展、新闻头条、我的等相应操作; 5、基础数据管理:实现系统基本信息的添加、修改及删除等操作,并且根据需求进行交流信息的查看及回复相应操作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值