[color=red]1.游标。为什么不赞成使用游标?(存储过程,索引,视图,函数)
2.http协议断点续传......
3.源代码配置管理
4.网络爬虫[/color]
[size=small][color=red]答案来自网络[/color][/size]
[color=red]1.如何在类外创建一个方法
不能
2.......
3.安卓常见面试题
4.String ,StringBuffer ,StringBulder 线程安全性比较[/color]
-------------------------------start------------------------------------
[color=red]String 和StringBuffer 的区别[/color]
JAVA平台提供了两个类: String和StringBuffer ,它们可以储存和操作字符串,即包含多个字符的字符数据。这个 String类提供了数值不可改变的字符串。而这个 StringBuffer类提供的字符串可以进行修改。当你知道字符数据要改变的时候你就可以使用 StringBuffer。典型地,你可以使用StringBuffers来动态构造字符数据。 另外,String实现了equals 方法,new String(“abc”).equals(new String(“abc”)的结果为true,而StringBuffer 没有实现 equals方法,所以,new StringBuffer(“abc”).equals(new StringBuffer(“abc”)的结果为false。
接着要举一个具体的例子来说明,我们要把 1到100 的所有数字拼起来,组成一个串。
StringBuffer sbf = new StringBuffer();
for(int i=0;i<100;i++) {
sbf.append(i);
}
上面的代码效率很高,因为只创建了一个 StringBuffer对象,而下面的代码效率很低,因为创建了 101个对象。
String str = new String();
for(int i=0;i<100;i++) {
str = str + i;
}
String覆盖了 equals方法和hashCode 方法,而 StringBuffer没有覆盖equals 方法和hashCode方法,所以,将 StringBuffer对象存储进Java集合类中时会出现问题。
在讲两者区别时,应把循环的次数搞成 10000,然后用endTime-beginTime 来比较两者执行的时间差异,最后还要 讲讲StringBuilder与StringBuffer 的区别。 (区别如下)
StringBuffer线程安全的可变字符序列。一个类似于 String 的字符串缓冲区,但不能修改。虽然在任意时间点上它都包含某种特定的字符序列,但通过某些方法调用可以改变该序列的长度和内容,可将字符串缓冲区安全地用于多个线程。可以在必要时对这些方法进行同步,因此任意特定实例上的所有操作就好像是以串行顺序发生的,该顺序与所涉及的每个线程进行的方法调用顺序一致。(从 JDK 5 开始,为该类补充了一个单个线程使用的等价类,即 StringBuilder。与该类相比,通常应该优先使用 StringBuilder 类,因为它支持所有相同的操作,但由于它不执行同步,所以速度更快。)
StringBuilder一个可变的字符序列。此类提供一个与 StringBuffer 兼容的 API ,但不保证同步。该类被设计用作 StringBuffer 的一个简易替换,用在字符串缓冲区被单个线程使用的时候(这种情况很普遍)。(如果可能,建议优先采用该类,因为在大多数实现中,它比 StringBuffer 要快。但将 StringBuilder 的实例用于多个线程是不安全的。如果需要这样的同步,则建议使用 StringBuffer。)
StringBuffer和 Stringbuilder上的主要操作都是 append 和 insert 方法,可重载这些方法,以接受任意类型的数据。每个方法都能有效地将给定的数据转换成字符串,然后将该字符串的字符追加或插入到字符串缓冲区中。 append 方法始终将这些字符添加到缓冲区的末端;而 insert 方法则在指定的点添加字符。
例如,如果 z 引用一个当前内容是“ start”的字符串缓冲区对象,则此方法调用 z.append("le") 会使字符串缓冲区包含“ startle”,而 z.insert(4, "le") 将更改字符串缓冲区,使之包含“ starlet”。
通常,如果 sb 引用 StringBuilder 的一个实例,则 sb.append(x) 和 sb.insert(sb.length(), x) 具有相同的效果。只要发生有关源序列(如在源序列中追加或插入)的操作,该类就只在执行此操作的字符串缓冲区上而不是在源上实现同步。
-------------------------------end------------------------------------
[color=red]5.集合框架Collection[/color]
Collection
├List
│├LinkedList
│├ArrayList
│└Vector
│ └Stack
└Set-|EnumSet , LinkedHashSet , HashSet , TreeSet
Map
├Hashtable
├HashMap
└WeakHashMap
琴弦第七——)java学习笔记
http://my.eoe.cn/794406
[color=red]6.class文件包含的内容[/color]
-----------------------class 文件概述start----------------------------------
-----------------------class 文件概述end----------------------------------
[color=red]7.实现一个简单的oa系统
8.如何try{}...catch(){}之后继续抛异常? [/color]
-----------------------涉及:java中异常处理机制 start-----------------------------
-----------------------java中异常处理机制 end----------------------------------
[color=red]9.如何声明一个常量
四种方式:
1.利用接口的方式(接口中变量默认static final)
2.利用枚举类型(Enum)
3.普通类声明中static final
4.利用方法获得类似于方法三
[/color]
-----------------------------深入jvm笔记---start-----------------------------------
-----------------------------深入jvm笔记---end-----------------------------------
2.http协议断点续传......
3.源代码配置管理
4.网络爬虫[/color]
[size=small][color=red]答案来自网络[/color][/size]
[color=red]1.如何在类外创建一个方法
不能
2.......
3.安卓常见面试题
4.String ,StringBuffer ,StringBulder 线程安全性比较[/color]
-------------------------------start------------------------------------
[color=red]String 和StringBuffer 的区别[/color]
JAVA平台提供了两个类: String和StringBuffer ,它们可以储存和操作字符串,即包含多个字符的字符数据。这个 String类提供了数值不可改变的字符串。而这个 StringBuffer类提供的字符串可以进行修改。当你知道字符数据要改变的时候你就可以使用 StringBuffer。典型地,你可以使用StringBuffers来动态构造字符数据。 另外,String实现了equals 方法,new String(“abc”).equals(new String(“abc”)的结果为true,而StringBuffer 没有实现 equals方法,所以,new StringBuffer(“abc”).equals(new StringBuffer(“abc”)的结果为false。
接着要举一个具体的例子来说明,我们要把 1到100 的所有数字拼起来,组成一个串。
StringBuffer sbf = new StringBuffer();
for(int i=0;i<100;i++) {
sbf.append(i);
}
上面的代码效率很高,因为只创建了一个 StringBuffer对象,而下面的代码效率很低,因为创建了 101个对象。
String str = new String();
for(int i=0;i<100;i++) {
str = str + i;
}
String覆盖了 equals方法和hashCode 方法,而 StringBuffer没有覆盖equals 方法和hashCode方法,所以,将 StringBuffer对象存储进Java集合类中时会出现问题。
在讲两者区别时,应把循环的次数搞成 10000,然后用endTime-beginTime 来比较两者执行的时间差异,最后还要 讲讲StringBuilder与StringBuffer 的区别。 (区别如下)
StringBuffer线程安全的可变字符序列。一个类似于 String 的字符串缓冲区,但不能修改。虽然在任意时间点上它都包含某种特定的字符序列,但通过某些方法调用可以改变该序列的长度和内容,可将字符串缓冲区安全地用于多个线程。可以在必要时对这些方法进行同步,因此任意特定实例上的所有操作就好像是以串行顺序发生的,该顺序与所涉及的每个线程进行的方法调用顺序一致。(从 JDK 5 开始,为该类补充了一个单个线程使用的等价类,即 StringBuilder。与该类相比,通常应该优先使用 StringBuilder 类,因为它支持所有相同的操作,但由于它不执行同步,所以速度更快。)
StringBuilder一个可变的字符序列。此类提供一个与 StringBuffer 兼容的 API ,但不保证同步。该类被设计用作 StringBuffer 的一个简易替换,用在字符串缓冲区被单个线程使用的时候(这种情况很普遍)。(如果可能,建议优先采用该类,因为在大多数实现中,它比 StringBuffer 要快。但将 StringBuilder 的实例用于多个线程是不安全的。如果需要这样的同步,则建议使用 StringBuffer。)
StringBuffer和 Stringbuilder上的主要操作都是 append 和 insert 方法,可重载这些方法,以接受任意类型的数据。每个方法都能有效地将给定的数据转换成字符串,然后将该字符串的字符追加或插入到字符串缓冲区中。 append 方法始终将这些字符添加到缓冲区的末端;而 insert 方法则在指定的点添加字符。
例如,如果 z 引用一个当前内容是“ start”的字符串缓冲区对象,则此方法调用 z.append("le") 会使字符串缓冲区包含“ startle”,而 z.insert(4, "le") 将更改字符串缓冲区,使之包含“ starlet”。
通常,如果 sb 引用 StringBuilder 的一个实例,则 sb.append(x) 和 sb.insert(sb.length(), x) 具有相同的效果。只要发生有关源序列(如在源序列中追加或插入)的操作,该类就只在执行此操作的字符串缓冲区上而不是在源上实现同步。
-------------------------------end------------------------------------
[color=red]5.集合框架Collection[/color]
Collection
├List
│├LinkedList
│├ArrayList
│└Vector
│ └Stack
└Set-|EnumSet , LinkedHashSet , HashSet , TreeSet
Map
├Hashtable
├HashMap
└WeakHashMap
琴弦第七——)java学习笔记
http://my.eoe.cn/794406
[color=red]6.class文件包含的内容[/color]
-----------------------class 文件概述start----------------------------------
当JVM运行Java程序的时候,它会加载对应的class文件,并提取class文件中的信息存放在JVM开辟出来的方法区内存中。那么这个class文件里面到底有些什么内容呢?
一、class文件内容概述
class文件是由8bits的字节流组成,全部字节构成了15个有意义的项目。这些项目之间没有任何无意义的字节,因此class文件非常紧凑。占据多字节空间的项目按照高位在前的顺序存放。下面我们详细讨论这些项目:
★ magic(魔数) 每个class文件的前4个字节称为魔数,值为0xCAFEBABE。作用在于轻松的辨别class文件与非class文件。
★ minor_version、major_version(次、主版本号) 各占2个字节。随着Java技术的发展,class文件的格式会发生变化。版本号的作用在于使得虚拟机能够认识当前加载class的文件格式。从而准确的提取class文件信息。
★ constant_pool_count 、constance_pool(常量池) 从这里开始的字节组成了常量池 。 存储了诸如符号常量、final常量值、基本数据类型的字面值等内容。JVM会将每一个常量构成一个常量表,每个常量表都有自己的入口地址。而实际上在JVM会将这些常量表存储在方法区中一块连续的内存空间中,因此class文件会根据常量表在常量池中的位置对其进行索引。比如常量池中的第一个常量表的索引值就是1,第二个就是2。有的时候常量表A需要常量表B的内容,则在常量表A中会存储常量表B的索引值x。而constant_pool_count就记录了有多少个常量表,或则所有多少个索引值。实际上,常量池中没有索引值为0的常量表,但这缺失的索引值也被记录在constant_pool_count中,因此 constant_pool_count等于常量表的数量加1。关于常量池的具体内容,我们会在下面详细讲述,并用一个例子来显示整个class文件的内容。
★ access_flags(访问标志) 占用2个字节。用来表明该class文件中定义的是类还是接口,访问修饰符是public还是缺省。类或接口是否是抽象的。类是否是final的。
★ this_class 占用2个字节。 它是一个对常量池的索引。指向的是常量池中存储类名符号引用的CONSTANT_Class_info常量表(见下面常量池具体结构)。比如this_class=0x0001。则表示指向常量池中的第一个常量表。通常这个表是指向当前class文件所定义的类名。
★ super_class 占用2个字节 与this_class类似,指向存放当前class文件所定义类的超类名字的索引的CONSTANT_Class_info常量表。
★ inteface_count、interfaces interface_count是class文件所定义的类直接实现的接口或父类实现的接口的数量。占2个字节。intefaces包含了对每个接口的 CONSTANT_Class_info常量表的索引。
★fields_count、fields fields_count表明了类中字段的数量 。fields是不同长度的field_info表的序列。这些field_info表中并不包含超类或父接口继承而来的字段。field_info表展示了一个字段的信息,包括字段的名字,描述符和修饰符。如果该字段是final的,那么还会展示其常量值。注意,这些信息有些存放在field_info里面,有些则存放在field_info所指向的常量池中。下面我们阐述一下这个field_info表的格式:
access_flags(2byte 访问修饰符)
name_index(2byte 存储字段名的常量表在常量池中的索引)
description_index(2byte 存储字段的所属类型的常量表在常量池中的索引)
attribute_count(2byte 属性表的数量)
attribute (属性)
其中attribute是由多个attribute_info组成。而JVM规范定义了字段的三种属性:ConstanceValue、Deprecated和Synthetic。
★method_count、methods 与字段类似,method_count表明类中方法的数量和每个方法的常量表的索引。methods表明了不同长度的method_info表的序列。该表格式如下:
access_flags(2byte 访问修饰符)
name_index(2byte 存储方法名的常量表在常量池中的索引)
description_index(2byte 存储方法的返回类型和参数类型的常量表在常量池中的索引)
attribute_count(2byte 属性表的数量)
attribute (属性)
其中方法的属性JVM规定了四种:Code,Deprecated,Exceptions,Synthetic。
二、常量池的具体结构
在Java程序中,有很多的东西是永恒的,不会在运行过程中变化。比如一个类的名字,一个类字段的名字/所属类型,一个类方法的名字/返回类型/参数名与所属类型,一个常量,还有在程序中出现的大量的字面值。比如下面小段源码红色显示的东西。
public class ClassTest {
private String itemS ="我们 ";
private final int itemI =100 ;
public void setItemS (String para ){...}
}
而这些在JVM解释执行程序的时候是非常重要的。那么编译器将源程序编译成class文件后,会用一部分字节分类存储这些永恒不变的红色东西。而这些字节我们就成为常量池。事实上,只有JVM加载class后,在方法区中为它们开辟了空间才更像一个“池”。
正如上面所示,一个程序中有很多永恒的红色东西。每一个都是常量池中的一个常量表(常量项)。而这些常量表之间又有不同,class文件共有11种常量表,如下所示:
常量表类型标志值(占1 byte)描述CONSTANT_Utf81UTF-8编码的Unicode字符串CONSTANT_Integer3int类型的字面值CONSTANT_Float4float类型的字面值CONSTANT_Long5long类型的字面值CONSTANT_Double6double类型的字面值CONSTANT_Class7对一个类或接口的符号引用CONSTANT_String8String类型字面值的引用CONSTANT_Fieldref9对一个字段的符号引用CONSTANT_Methodref10对一个类中方法的符号引用CONSTANT_InterfaceMethodref11对一个接口中方法的符号引用CONSTANT_NameAndType12对一个字段或方法的部分符号引用(1) CONSTANT_Utf8 用UTF-8编码方式来表示程序中所有的重要常量字符串。这些字符串包括: ①类或接口的全限定名, ②超类的全限定名,③父接口的全限定名, ④类字段名和所属类型名,⑤类方法名和返回类型名、以及参数名和所属类型名。⑥字符串字面值
表格式: tag(标志1:占1byte) length(字符串所占字节的长度,占2byte) bytes(字符串字节序列)
(2) CONSTANT_Integer、 CONSTANT_Float、 CONSTANT_Long、 CONSTANT_Double 所有基本数据类型的字面值。比如在程序中出现的1用CONSTANT_Integer表示。3.1415926F用 CONSTANT_Float表示。
表格式: tag bytes(基本数据类型所需使用的字节序列)
(3) CONSTANT_Class 使用符号引用来表示类或接口。我们知道所有类名都以 CONSTANT_Utf8表的形式存储。但是我们并不知道 CONSTANT_Utf8表中哪些字符串是类名,那些是方法名。因此我们必须用一个指向类名字符串的符号引用常量来表明。
表格式: tag name_index(给出表示类或接口名的CONSTANT_Utf8表的索引)
(4) CONSTANT_String 同 CONSTANT_Class,指向包含字符串字面值的 CONSTANT_Utf8表。
表格式: tag string_index(给出表示字符串字面值的CONSTANT_Utf8表的索引)
(5) CONSTANT_Fieldref 、 CONSTANT_Methodref、 CONSTANT_InterfaceMethodref 指向包含该字段或方法所属类名的 CONSTANT_Utf8表,以及指向包含该字段或方法的名字和描述符的 CONSTANT_NameAndType表
表格式: tag class _index(给出包含所属类名的CONSTANT_Utf8表的索引) name_and_type_index(包含字段名或方法名以及描述符的 CONSTANT_NameAndType表 的索引)
(6) CONSTANT_NameAndType 指向包含字段名或方法名以及描述符的 CONSTANT_Utf8表。
表格式: tag name_index(给出表示字段名或方法名的CONSTANT_Utf8表的索引) type_index(给出表示描述符的CONSTANT_Utf8表的索引)
下面是我将一个源程序编译成class文件后,对文件中的每一个字节的分析,可以更好的理解class文件的内容以及常量池的组成。
三、TestClass.class 文件实例分析
Java代码
1. //源代码
2. package hr.test;
3. //ClassTest类
4. public class ClassTest {
5. private int itemI=0; //itemI类字段
6. private static String itemS="我们"; //itemS类字段
7. private final float PI=3.1415926F; //PI类字段
8. //构造器方法
9. public ClassTest(){
10. }
11. //getItemI方法
12. public int getItemI(){
13. return this.itemI;
14. }
15. //getItemS方法
16. public static String getItemS(){
17. return itemS;
18. }
19. //main主方法
20. public static void main(String[] args) {
21. ClassTest ct=new ClassTest();
22. }
23. }
TestClass.class 字节码分析(字节顺序从上到下,从左到右。每个字节用一个0-255的十进制整数表示)
202 254 186 190 -- 魔数
0 0 -- 次版本号
0 50 -- 主版本号
0 43 -- 常量池中常量表的数量有42个,下面红色括号中的数据表明该常量表所在常量池中的索引,从索引1开始
(1) 7 0 2 -- 对类ClassTest的符号引用(7为标志 02指向了常量池的索引2的位置)
(2) 1 0 17 104 114 47 116 101 115 116 47 67 108 97 115 115 84 101 115 116 -- 类全限定名hr\test\ClassTest
(3) 7 0 4 -- 对类Object的符号引用
(4) 1 0 16 106 97 118 97 47 108 97 110 103 47 79 98 106 101 99 116 -- 超类全限定名 java/lang/Object
(5) 1 0 5 105 116 101 109 73 -- 第1个类字段名 itemI
(6) 1 0 1 73 -- I 第1个类字段类型为整型
(7) 1 0 5 105 116 101 109 83 -- 第2个类字段名 itemS
(8) 1 0 18 76 106 97 118 97 47 108 97 110 103 47 83 116 114 105 110 103 59 -- 第2个类字段类型的全限定名 Ljava/lang/String
(9) 1 0 2 80 73 -- 第3个类字段名PI
(10) 1 0 1 70 -- 第3个类字段类型为float
(11) 1 0 13 67 111 110 115 116 97 110 116 86 97 108 117 101 --- 第3个类字段为常量ConstantValue
(12) 4 64 73 15 218 -- 第3个类字段float字面值,占4bytes(3.1415926)
(13) 1 0 8 60 99 108 105 110 105 116 62 -- <clinit> 初始化方法名
(14) 1 0 3 40 41 86 -- ()V 方法的返回类型为void
(15) 1 0 4 67 111 100 101 -- Code
(16) 8 0 17 -- String字符串字面值(0 17表示索引1 7)
(17) 1 0 6 230 136 145 228 187 172 -- "我们"
(18) 9 0 1 0 19 -- 指向 第2个 字段的引用(0 1指向索引1,0 19指向索引19)
(19) 12 0 7 0 8 --指向 第2个 字段的名字和描述符的索引,
(20) 1 0 15 76 105 110 101 78 117 109 98 101 114 84 97 98 108 101 -- LineNumberTable
(21) 0 18 76 111 99 97 108 86 97 114 105 97 98 108 101 84 97 98 108 101 -- LocalVariableTable
(22) 1 0 6 60 105 110 105 116 62 -- <init> 表示初始化方法名
(23) 10 0 3 0 24 -- 指向父类Object的构造器方法,0 3表示父类名常量表的索引,0 24表示存放该方法名称和描述符的引用的常量表的索引
(24) 12 0 22 0 14 -- 指向方法名和描述符的常量表的索引。0 22是方法名的常量表索引,0 14是描述符的常量表索引
(25) 9 0 1 0 26 -- 指向第1个字段的引用, 0 1表示字段所属类型的索引,0 26表示字段名和描述符的索引
(26) 12 0 5 0 6 -- 指向第1个字段的名字和描述符的索引
(27) 9 0 1 0 28 -- 指向第3个字段的引用, 0 1表示字段所属类型的索引,0 28表示字段名和描述符的索引
(28) 12 0 9 0 10 -- 指向第3个字段的名字和描述符的索引
(29) 1 0 4 116 104 105 115 -- 隐含参数符号this
(30) 1 0 11 76 67 108 97 115 115 84 101 115 116 59 -- LClassTest;
(31) 1 0 8 103 101 116 73 116 101 109 73 - - 方法名 getItemI
(32) 1 0 3 40 41 73 -- ()I 方法描述符:返回类型int
(33) 1 0 8 103 101 116 73 116 101 109 83 -- 方法名 getItemS
(34) 1 0 20 40 41 76 106 97 118 97 47 108 97 110 103 47 83 116 114 105 110 103 59 --- 方法描述符()Ljava/lang/String;
(35) 1 0 4 109 97 105 110 -- 主方法名main
(36) 1 0 22 40 91 76 106 97 118 97 47 108 97 110 103 47 83 116 114 105 110 103 59 41 86 --- ()Ljava/lang/String;)V 主方法中的参数的字符串数组类型名
(37) 10 0 1 0 24 指向当前 ClassTest 类的构造器方法,0 1表示存放当前类名的常量表的索引。0 24是存放方法名和描述符的符号引用的常量表索引。
(38) 1 0 4 97 114 103 115 -- 参数args
(39) 1 0 19 91 76 106 97 118 97 47 108 97 110 103 47 83 116 114 105 110 103 59 -- 字符串数组 [Ljava/lang/String;
(40) 1 0 2 99 116 --- 对象符号ct
(41) 1 0 10 83 111 117 114 99 101 70 105 108 101 -- SourceFile
(42) 1 0 14 67 108 97 115 115 84 101 115 116 46 106 97 118 97 -- ClassTest.java
0 33 ---- access_flag 访问标志 public
0 1 ---- this_class 指向当前类的符号引用在常量池中的索引
0 3 ---- super_class
0 0 ---- inteface_count接口的数量
0 3 --- field_count字段的数量
// 字段 itemI
0 2 ---- private 修饰符
0 5 ---- 字段名在常量池中的索引,字段itemI
0 6 ---- 字段的描述符(所属类型)在常量池中的索引
0 0 --- 字段的属性信息表(attribute_info)的数量
// 字段 itemS
0 10 ---- private static 修饰符
0 7 ---字段名在常量池中的索引,字段itemS
0 8 ---字段的描述符(所属类型)在常量池中的索引
0 0 --- 字段的属性信息表(attribute_info)的数量
// 字段 PI
0 18 -- private final 修饰符
0 9 ---字段名在常量池中的索引,//字段PI
0 10 ---字段的描述符(所属类型)在常量池中的索引
0 1 --- 字段的属性信息表(attribute_info)的数量
0 11 --- 属性名在常量池中的索引。即ConstantValue
0 0 0 2 --- 属性所占的字节长度
0 12 --- 属性值在常量池中的索引。即常量字面值
0 5 -- Method_count方法的数量
//类的静态数据初始化方法<clinit>
0 8 ---- static 修饰符(所有的初始化方法都是static的)
0 13 --- 在常量池中的索引。初始化方法名<clinit>,该方法直接由JVM在特定的时候调用,并非由字节码生成。
0 14 --- 在常量池中的索引。返回类型为void。
0 1 --- 属性数量
0 15 -- 属性名 在常量池中的索引。即code
0 0 0 42 --- 属性所占的字节长度2
0 1 0 0 0 0 0 6 18 16 179 0 18 177 0 0 0 2 0 20 0 0 0 10 0 2 0 0 0 5 0 5 0 2 0 21 0 0 0 2 0 0 ---该方法的字节码指令序列和其他信息
//类的普通实例数据的初始化方法,针对类构造器生成的<init>方法。
0 1 --- public 修饰符
0 22 --- 构初始化方法名<init>
0 14 --- 构造器的返回类型为void
0 1 --- 属性数量
0 15 --- 属性名在常量池中的索引。即Code
0 0 0 70 -- 属性所占的字节长度70
0 2 0 1 0 0 0 16 42 183 0 23 42 3 181 0 25 42 18 12 181 0 27 177 0 0 0 2 0 200 0 0 18 0 4 0 0 0 8 0 4 0 4 0 9 0 6 0 15 0 9 0 21 0 0 0 12 0 10 0 0 16 0 29 0 30 0 0 ---该方法的字节码指令序列和其他信息
//getItemI方法
0 1 --- public 修饰符
0 31 --- 在常量池中的索引。方法名getItemI
0 32 --- 在常量池中的索引。方法返回类型为int
0 1 -- 属性数量
0 15 --- 属性名在常量池中的索引。即Code
0 0 0 47 --- 属性所占的字节长度70
0 1 0 1 0 0 0 5 42 180 0 25 172 0 0 0 2 0 20 0 0 0 6 0 1 0 0 0 12 0 21 0 0 0 12 0 1 0 0 0 5 0 29 0 30 0 0 --- 该方法的字节码指令序列和其他信息
//getItemS方法
0 9 --- public static 修饰符
0 33 --- 在常量池中的索引。方法名getItemS
0 34 - -- 在常量池中的索引。方法返回类型为String
0 1 --- 属性数量
0 15 -- 属性名在常量池中的索引。即Code
0 0 0 36 --- 属性所占的字节长度36
0 1 0 0 0 0 0 4 178 0 18 176 0 0 0 2 0 20 0 0 0 6 0 1 0 0 0 16 0 21 0 0 0 2 0 0 --该方法的字节码指令序列和其他信息
//main方法
0 9 --- public static 修饰符
0 35 --- 在常量池中的索引。主方法名main
0 36 -- 在常量池中的索引。方法返回类型为String[]
0 1 --- 属性数量
0 15 --- 属性名在常量池中的索引。即Code
0 0 0 65 --- 属性所占的字节长度36
0 2 0 2 0 0 0 9 187 0 1 89 183 0 37 76 177 0 0 0 2 0 20 0 0 0 10 0 2 0 0 0 20 0 8 0 21 0 21 0 0 0 22 0 2 0 0 0 9 0 38 0 39 0 0 0 8 0 1 0 40 0 30 0 1 0 1 0 41 0 0 0 2 0 42
我们分析上面的字节码例子,不难看出:
蓝色背景的常量池字节码区域:
(1) 所有的字面值都是存放在常量池中的。 特别注意的是“我们”这个字符串常量也是在常量池中的。如果一个程序出现多个“我们”,那么常量池中也只会有一个。另外,也正是因为“我们”存放在常量池中,使得一些字符串的==比较变的需要琢磨了。
(2)ClassTest并没有任何显示的父类。但在常量池中,我们发现有Object的符号常量存在。 这也证实了在Java中,任何类都直接或间接继承了Object的,而Object并不需要在代码中显示继承,JVM会帮我们做到这一点。
(3)常量池中有一个隐含参数this的符号常量。即使程序中不存在this,JVM也会悄悄的设置一个这样的对象。
绿色背景的类字段字节码区域:
(1)字段PI是浮点型常量,在编译期的字节码中就已经指定好了PI的字面值存储在常量池中的某个索引内 。这一点也证实了Java中的常量在编译期就已经得到了值,在运行过程中是无法改变的。
橙色背景的类方法字节码区域:
(1)主方法main是作为ClassTest的类方法存在的,在字节码中main和其他的类方法并没有什么区别。 实际上,我们也确实可以通过ClassTest.main(..)来调用ClassTest中的main方法。
(2)在class文件常量池字节码中有两个比较特别的方法名符号:<clinit>和<init>。其中<clinit>方法是编译器自己生成的,编译器会把类静态变量的直接初始化语句和静态初始化语句块的代码都放到了class文件的<clinit>方法中。而对所有非静态非常量数据域的初始化工作要靠<init>方法来完成。针对每一个类的构造方法,编译器都会产生一个<init>方法。即使是缺省构造器也不例外。
博客源文件地址:http://hxraid.iteye.com/blog/687660
-----------------------class 文件概述end----------------------------------
[color=red]7.实现一个简单的oa系统
8.如何try{}...catch(){}之后继续抛异常? [/color]
-----------------------涉及:java中异常处理机制 start-----------------------------
前言:
java 中的异常处理机制你真的理解了吗?掌握了吗?
catch 体里遇到 return 是怎么处理? finally 体遇到 return 怎么办?finally 体里有 System.exit() 方法怎么处理?当 catch 和 finally 体里同时遇上 return 怎么办?
相信你在处理异常的时候不是每次都把它 throws 掉就完事了,很多时候异常是需要我们自己来 catch 并针对所抛出的 Exception 做一些后续的处理工作。
直接上代码,先贴下面测试需要调用的方法:
1
2 // catch 后续处理工作
3 public static boolean catchMethod() {
4 System.out.print("call catchMethod and return --->> ");
5 return false;
6 }
7 // finally后续处理工作
8 public static void finallyMethod() {
9 System.out.println();
10 System.out.print("call finallyMethod and do something --->> ");
11 }
12
1. 抛出 Exception,没有 finally,当 catch 遇上 return
1
2public static boolean catchTest() {
3 try {
4 int i = 10 / 0; // 抛出 Exception,后续处理被拒绝
5 System.out.println("i vaule is : " + i);
6 return true; // Exception 已经抛出,没有获得被执行的机会
7 } catch (Exception e) {
8 System.out.println(" -- Exception --");
9 return catchMethod(); // Exception 抛出,获得了调用方法并返回方法值的机会
10 }
11 }
12
后台输出结果:
1
2 -- Exception --
3call catchMethod and return --->> false
4
2. 抛出 Exception,当 catch 体里有 return,finally 体的代码块将在 catch 执行 return 之前被执行
1
2public static boolean catchFinallyTest1() {
3 try {
4 int i = 10 / 0; // 抛出 Exception,后续处理被拒绝
5 System.out.println("i vaule is : " + i);
6 return true; // Exception 已经抛出,没有获得被执行的机会
7 } catch (Exception e) {
8 System.out.println(" -- Exception --");
9 return catchMethod(); // Exception 抛出,获得了调用方法的机会,但方法值在 finally 执行完后才返回
10 }finally{
11 finallyMethod(); // Exception 抛出,finally 代码块将在 catch 执行 return 之前被执行
12 }
13 }
14
后台输出结果:
1
2 -- Exception --
3call catchMethod and return --->>
4call finallyMethod and do something --->> false
5
3. 不抛 Exception,当 finally 代码块里面遇上 return,finally 执行完后将结束整个方法
1
2public static boolean catchFinallyTest2() {
3 try {
4 int i = 10 / 2; // 不抛出 Exception
5 System.out.println("i vaule is : " + i);
6 return true; // 获得被执行的机会,但执行需要在 finally 执行完成之后才能被执行
7 } catch (Exception e) {
8 System.out.println(" -- Exception --");
9 return catchMethod();
10 }finally{
11 finallyMethod();
12 return false; // finally 中含有 return 语句,这个 return 将结束这个方法,不会在执行完之后再跳回 try 或 catch 继续执行,方法到此结束,返回 false
13 }
14 }
15
后台输出结果:
1
2i vaule is : 5
3
4call finallyMethod and do something --->> false
5
4. 不抛 Exception,当 finally 代码块里面遇上 System.exit() 方法 将结束和终止整个程序,而不只是方法
1
2public static boolean finallyExitTest() {
3 try {
4 int i = 10 / 2; // 不抛出 Exception
5 System.out.println("i vaule is : " + i);
6 return true; // 获得被执行的机会,但由于 finally 已经终止程序,返回值没有机会被返回
7 } catch (Exception e) {
8 System.out.println(" -- Exception --");
9 return true;
10 }finally {
11 finallyMethod();
12 System.exit(0);// finally 中含有 System.exit() 语句,System.exit() 将退出整个程序,程序将被终止
13 }
14 }
15
后台输出结果:
1
2i vaule is : 5
3
4call finallyMethod and do something --->>
5
5. 抛出 Exception,当 catch 和 finally 同时遇上 return,catch 的 return 返回值将不会被返回,finally 的 return 语句将结束整个方法并返回
1
2public static boolean finallyTest1() {
3 try {
4 int i = 10 / 0; // 抛出 Exception,后续处理被拒绝
5 System.out.println("i vaule is : " + i);
6 return true; // Exception 已经抛出,没有获得被执行的机会
7 } catch (Exception e) {
8 System.out.println(" -- Exception --");
9 return true; // Exception 已经抛出,获得被执行的机会,但返回操作将被 finally 截断
10 }finally {
11 finallyMethod();
12 return false; // return 将结束整个方法,返回 false
13 }
14 }
15
后台输出结果:
1
2 -- Exception --
3
4call finallyMethod and do something --->> false
5
6. 不抛出 Exception,当 finally 遇上 return,try 的 return 返回值将不会被返回,finally 的 return 语句将结束整个方法并返回
1
2public static boolean finallyTest2() {
3 try {
4 int i = 10 / 2; // 不抛出 Exception
5 System.out.println("i vaule is : " + i);
6 return true; // 获得被执行的机会,但返回将被 finally 截断
7 } catch (Exception e) {
8 System.out.println(" -- Exception --");
9 return true;
10 }finally {
11 finallyMethod();
12 return false; // return 将结束这个方法,不会在执行完之后再跳回 try 或 catch 继续执行,返回 false
13 }
14 }
15
后台输出结果:
1
2i vaule is : 5
3
4call finallyMethod and do something --->> false
5
结语:
(假设方法需要返回值)
java 的异常处理中,
在不抛出异常的情况下,程序执行完 try 里面的代码块之后,该方法并不会立即结束,而是继续试图去寻找该方法有没有 finally 的代码块,
如果没有 finally 代码块,整个方法在执行完 try 代码块后返回相应的值来结束整个方法;
如果有 finally 代码块,此时程序执行到 try 代码块里的 return 语句之时并不会立即执行 return,而是先去执行 finally 代码块里的代码,
若 finally 代码块里没有 return 或没有能够终止程序的代码,程序将在执行完 finally 代码块代码之后再返回 try 代码块执行 return 语句来结束整个方法;
若 finally 代码块里有 return 或含有能够终止程序的代码,方法将在执行完 finally 之后被结束,不再跳回 try 代码块执行 return。
在抛出异常的情况下,原理也是和上面的一样的,你把上面说到的 try 换成 catch 去理解就 OK 了 *_*
-----------------------java中异常处理机制 end----------------------------------
[color=red]9.如何声明一个常量
四种方式:
1.利用接口的方式(接口中变量默认static final)
2.利用枚举类型(Enum)
3.普通类声明中static final
4.利用方法获得类似于方法三
[/color]
-----------------------------深入jvm笔记---start-----------------------------------
java不仅仅是一门编程语言还是一门技术,包含四方面:java编程语言,java类文件格式,java虚拟机和java应用程序接口(java API) 。这几个方面的关系:
JVM在它的生存周期中有一个明确的任务,那就是运行 Java程序,因此 当Java程序启动的时候,就产生 JVM的一个实例;当程序运行结束的时候,该实例也跟着消失了。
每个 JVM都有两种机制,一个是装载具有合适名称的类 (类或是接口 ),叫做 类装载子系统 ;另外的一个负责执行包含在已装载的类或接口中的指令,叫做 运行引擎。每个 JVM又包括方法区、堆、 Java栈、程序计数器和本地方法栈这五个部分,这几个部分和类装载机制与运行引擎机制一起组成的体系结构 图为:
jvm的体系结构图
JVM的每个实例都有一个它自己的方法域和一个堆,运行于 JVM内的所有的线程都共享这些区域;当虚拟机装载类文件的时候,它解析其中的二进制数据所包含的类信息,并把它们放到方法域中;当程序运行的时候, JVM把程序初始化的所有对象置于堆上;而每个线程创建的时候,都会拥有自己的程序计数器和 Java栈, ( 其中程序计数器中的值指向下一条即将被执行的指令,线程的 Java栈则存储为该线程调用Java方法的状态;本地方法调用的状态被存储在本地方法栈,该方法栈依赖于具体的实现 ) 。
Java不规定具体使用的垃圾回收算法 ,可以根据系统的需求使用各种各样的算法。
每个线程一旦被创建就拥有了自己的程序计数器。当 线程执行Java方法的时候,它包含该线程正在被执行的指令的地址。但是若线程执行的是一个本地的方法,那么程序计数器的值就不会被定义。
java虚拟机的栈有三个区域:局部变量区,运行环境区,操作数区。
关于java的异常处理机制:
异常捕捉
异常情况在Java中被称作Error(错误)或Exception(异常),是Throwable类的子类,在程序中的原因是:①动态链接错,如无法找到所需的class文件。②运行时错,如对一个空指针的引用。程序使用了throw语句。
当异常发生时,Java虚拟机采取如下措施:
• 检查与当前方法相联系的catch子句表。每个catch子句包含其有效指令范围,能够处理的异常类型,以及处理异常的代码块地址。
• 与异常相匹配的catch子句应该符合下面的条件:造成异常的指令在其指令范围之内,发生的异常类型是其能处理的异常类型的子类型。如果找到了匹配的catch子句,那么系统转移到指定的异常处理块处执行;如果没有找到异常处理块,重复寻找匹配的catch子句的过程,直到当前方法的所有嵌套的 catch子句都被检查过。
• 由于虚拟机从第一个匹配的catch子句处继续执行,所以catch子句表中的顺序是很重要的。因为Java代码是结构化的,因此总可以把某个方法的所有的异常处理器都按序排列到一个表中,对任意可能的程序计数器的值,都可以用线性的顺序找到合适的异常处理块,以处理在该程序计数器值下发生的异常情况。
• 如果找不到匹配的catch子句,那么当前方法得到一个"未截获异常"的结果并返回到当前方法的调用者,好像异常刚刚在其调用者中发生一样。如果在调用者中仍然没有找到相应的异常处理块,那么这种错误将被传播下去。如果错误被传播到最顶层,那么系统将调用一个缺省的异常处理块。
java虚拟机的执行过程:java virtual machine 调用某个指定类的main方法---->传递字符串参数(是指定的类被装载同时衔接该类所使用的其他类型)
Class Loader只管加载,只要符合文件结构就加载,至于说能不能运行,则不是它负责的,那是由 Execution Engine负责的。
Stack 栈
栈也叫栈内存,是Java程序的运行区,是在线程创建时创建,它的生命期是跟随线程的生命期,线程结束栈内存也就释放,对于栈来说不存在垃圾回收问题,只要线程一结束,该栈就Over。问题出来了:栈中存的是那些数据呢?又什么是格式呢?
栈中的数据都是以栈帧(Stack Frame)的格式存在,栈帧是一个内存区块,是一个数据集,是一个有关方法(Method)和运行期数据的数据集,当一个方法A被调用时就产生了一个栈帧F1,并被压入到栈中,A方法又调用了B方法,于是产生栈帧F2也被压入栈,执行完毕后,先弹出F2栈帧,再弹出F1栈帧,遵循“先进后出”原则。
那栈帧中到底存在着什么数据呢?栈帧中主要保存3类数据:本地变量(Local Variables),包括输入参数和输出参数以及方法内的变量;栈操作(Operand Stack),记录出栈、入栈的操作;栈帧数据(Frame Data),包括类文件、方法等等。
Heap堆内存分为三个部分:
·1.永久存储区,用于存放自身所携带的Class,interface的源数据,此区域的数据不会被垃圾回收,关闭JVM才回释放该区域数据所占用的内存。
·2.新生区,新生区是类的诞生、成长、消亡的区域,一个类在这里产生,应用,最后被垃圾回收器收集,结束生命.
3.养老区用于保存从新生区筛选出来的 JAVA对象,一般池对象都在这个区域活跃。
Method Area 方法区
方法区是被所有线程共享,该区域保存所有字段和方法字节码,以及一些特殊方法如构造函数,接口代码也在此定义。
堆和栈:堆是在java虚拟机启动的时候创建,主要存放new出来的对象(包括对象变量以及对象方法)以及数组对象,但是对象中的临时变量是存在栈中的。
栈是跟随线程的,有线程就有栈,堆是跟随JVM的,有JVM就有堆内存。
类变量和实例变量:静态变量存在方法区中,实例变量存在堆内存中。
为什么会产生stackOverFlowError:一个线程把stack内存全部耗尽了,一般是递归函数造成的。
jvm中除了Heap 和Method Area是共享的其他的都是私有的。
JIT:java文件被编译成字节码(针对JVM的)--->翻译成原生码才能被机器执行。Just in time JVM中提供一个工具,把字节码编译成原生码,以后你来访问的时候直接访问原生码。
关于class文件:Class文件中除了有类的版本、字段、方法、接口等描述等信息外,还有一项信息是常量表 (constant_pool table),用于存放编译期已可知的常量,这部分内容将在类加载后进入方法区(永久代)存放。
-----------------------------深入jvm笔记---end-----------------------------------