1. sql(Structured Query Language)是关系型数据库的基本操作语言。
包括数据库查询(Data Query),数据操纵(Data Manipulation),数据定义(Data Definition)和数据控制(Data Control)。
数据库查询(Data Query):用于检索数据,也就是select语句。
数据操纵(Data Manipulation Language DML):用于改变数据,包括insert,update和delete。DML不会自动提交事物。
事物控制语句(Transactional Control Language TCL):用于维护数据的一致性,包括commit, rollback和savepoint;其中savepoint语句用于设置保存点,以恢复部分数据的更改。
数据定义(Data Definition Language DDL):用于建立,修改和删除数据对象。create,alter和 drop;DDL语句会自动提交事物。
数据控制(Data Control Language DCL):用于分配和收回权限,包括grant和revoke。DCL也会自动提交事物。
2.sql语句规则
sql语句的关键子不区分大小写;对象名和列名不区分大小写。
3.pl/sql 是oracle在标准sql基础上的扩展,允许定义变量和常量,允许过程语言结构(条件分支和循环)允许异常处理。
4.distinct 作用是要查询的所有数据,而不仅仅是紧跟它后面的那列。
selecte distinct a,b from table; --返回数据的条件是a相等且b必须相等distinct 可否放在列的中间那?
5.NVL(expr1,expr2)
如果expr1是null,则返回expr2,否则返回expr1,expr1和expr2可以是任意数据类型,但两者必须匹配。
NVL2(expr1,expr2,expr3)
如果expr1不是null则返回expr2,否则返回expr3。expr1可以是任意数据类型,而expr2和expr2可以是除LONG之外的其它类型。但是三者数据类型必须一致。
6.插入数据
不使用列插入单行数据:必须提供所有列的数据,数据的顺序与列的顺序一致。
insert into emp values(1,'gaci','man');
使用列插入单行数据:对于未出现的列,默认值为null
insert into emp(id,name,sex) values(1,'gaci','man');
使用只查询插入数据:--将表emp2的数据全部复制到emp1里面
insert into emp1(id,name,sex) select id,name,sex from emp2;
insert /*+APPEND*/into emp1(id,name,sex) select id,name,sex from emp2;
/*+APPEND*/表示直接装载方式,主要用于操作大批量数据
delete删除表中的所有数据时,不会释放表所占用的空间;但是可以回退(roll back)
truncate不仅删除表的数据,而其释放其空间;不可以回退。
7. 事务控制语句
事务用于确保数据库的一致性,有一组相关的DML语句组成。事务确保该组DML语句所执行的操作要么全部执行成功,要么全部取消。
事务主要有insert,update,delete和selete ... for update 组成,当执行这些语句时事务开始;当执行commit货rollback的时候事务结束。
锁
当执行事务操作时,oracle会在被操作的表上家上锁,以防止其它用户改变表结构;同时会在被操作的行上加上锁,以防止其它事务在相应的行上执行DML操作。
当执行commit语句之后,会确认事务变化(更新数据等), 结束事务,删除保存点,释放锁。
当执行DDl语句时会自动提交事物,create , alter,drop 等。
当执行DCL语句Grant,revoke等
当退化出sql/plus,pl/sql等
回退事务
保存点(savepoint)--是事务中的一点,用于取消部分事务。
设置保存点:savepoint a;
回退保存点:rollback to a ; -- 会取消a和面的操作
取消全部事务:rollback;
只读事务(会话级别的) set transaction read only;
只允许执行查询操作,不允许执行任何DML操作。尽管其它用户会话可能提交新事务,但只读事务不会取得新的数据变化,可以确保用户取得特定时间点的数据信息。
顺序事务 set transaction isolation level serializable;
只读事务可以使用户取得特定时间点的数据信息,但是该用户在只读事务期间不能执行dml操作。而顺序事务则可以这样。
数据分组
数据分组通过使用group by字句,分组函数和having字句共同实现。group by指定要分组的列,分组函数用于统计结果,having用户限制分组显示结果。
分组函数
只能出现在选择列表,order by 和 having字句中,不能出现在where和group by字句中
除了count(*)函数外,其它分组函数都会忽略null行
当执行select语句时,如果选择列表同时包括列,表达式和分组函数,那么这些列和表达式必须出现在group by字句中。
脏数据---未提交事务
--查询带有下划线的名字
select name from emp where name like '%a_%' ESCAPE 'a' -- a为转义字符
1. 块(Block)是pl/sql的基本程序单元,编写pl/sql程序实际上就是编写pl/sql块
块由定义部分,执行部分和异常处理部分组成。
定义部分用于定义常量,变量,游标,异常,复杂数据类型等,Declare。
执行部分用于实现应用模块功能,主要包括要执行的sql语句和pl/sql语句,begin。
异常处理用于处理 块 运行时可能出现的错误,Exception。
end是pl/sql的结束标记;declare,begin和exception后面没有分号,而end后面有。
2. pl/sql标识符命名规范
当使用标识符定义变量,常量时每行只能定义一个标识符;标识符只能使用字母,数字
下划线,$和#,如要使用其它字符则必须加上双引号;标识符名称必须以字符(A-Z a-z)
开头,并且最大长度30个字符,如果以其他字符开头,必须加上双引号;不能使用oracle关键字
,如果要使用则必须加上双引号。
注意:如果把一个带有单引号的字符串赋给一个变量,可以使用 q'[要赋的值]' 格式;
s_var := q'[i'm a boy!]'; -- 把 i'm a boy! 赋给变量s_var
3. pl/sql的块划分为匿名块,命名块,子程序和触发器。
匿名块:没有名字的pl/sql块。
- DECLARE
- v_name varchar2(10);
- BEGIN
- SELECT t.name INTO v_name FROM emp t WHERE t.id = &id;
- dbms_output.put_line('名字是:' || v_name);
- END;
- 3. 命名块:有名字(标示)的pl/sql,块前使用《》加以标示
- <<block1>>
- DECLARE
- v_name varchar2(10);
- v_age number(4);
- BEGIN
- <<block2>>
- BEGIN
- SELECT t.name INTO v_name FROM emp t WHERE t.id = &id;
- dbms_output.put_line('名字是:' || v_name);
- END;
- SELECT t.age INTO v_age FROM emp t WHERE t.name = v_name;
- dbms_output.put_line('年龄:' || v_age);
- END;
3.子程序:包括存储过程,函数和包
存储过程:用于执行特定的操作,可以指定输入参数(in)或者输出参数(out),不指定默认输入。
- CREATE PROCEDURE test_update(name VARCHAR2, age NUMBER) IS
- BEGIN
- UPDATE emp t SET t.age = number WHERE t.name = name;
- COMMIT;--这里不加可以吗?
- END;
函数:用于返回特定数据。函数的头部必须包含return字句,函数体内必须包含return语句返回的数据
- CREATE FUNCTION test_function(name VARCHAR2, age NUMBER) RETURN VARCHAR2 IS
- v_addr VARCHAR2(100);
- BEGIN
- SELECT '中国.山东' || t.addr
- INTO v_addr
- FROM emp t
- WHERE t.name = name
- and t.age = age;
- RETURN v_addr;
- END;
包:用于逻辑组合相关的存储过程和函数,由包规范和包体组成。包规范用于定义公用的常量, 变量,存储过程和函数。
- -- 包规范知识定义了一个存储过程和函数而没有实现代码
- CREATE PACKAGE test_pkg IS
- PROCEDURE test_procedure(name VARCHAR2, age NUMBER);
- FUNCTION test_function(name VARCHAR2, age NUMBER) RETURN NUMBER;
- END;
包体:
- CREATE PACKAGE BODY test_pkg IS
- PROCEDURE test_procedure(name VARCHAR2, age NUMBER) IS
- BEGIN
- UPDATE emp t set t.name = name WHERE t.age = age;
- END;
- FUNCTION test_function(name VARCHAR2, age NUMBER) RETURN NUMBER IS
- v_slary NUMBER(4, 1);
- BEGIN
- SELECT t.slary
- INTO v_slary
- WHERE t.name = name
- and t.age = age;
- RETURN NUMBER v_slary;
- END;
- END;
在使用包中的存储过程和函数时,必须在存储过程或者函数名之前加上包名作为前缀
(包名.子程序名)
4. 触发器:隐形执行的存储过程,必须指定触发事件以及触发操作,触发事件包括insert,update和delete;而触发操作实际上是一个pl/sql块。
- -- 当更新dept表中的d_name列时更新emp表中的d_name列
- REATE TRIGGER test_trigger
- AFTER UPDATE OF d_name ON dept
- FOR EACH ROW
- BEGIN
- UPDATE emp t SET t.d_name = :new.d_name WHERE t.d_name = o :d_name;
- END;
5. pl/sql数据类型:标量(Scalar)类型,复合(Composit)类型,参照(Reference)类型和LOB(Large Object)类型。
标量(Scalar)类型:只能存放单个数值的变量;定义标量时必须指定数据类型。
varchar2(n) : 定义可变长字符串,最大值32767字节;定义变量时必须指定长度。varchar2定义 列时最大长度是4000字节。
char(n) : 定义变量时如果没有指的那个长度默认为1, 定义列数据时最大长度是2000字节。
boolean :布尔类型值为true,false和null。表列不能使用该数据类型
binary_integer:定义整数-2147483647到2147483647
binary_float 和 binary_double 是oracle10新增,分别用来定义单精度浮点数和双精度浮点数
binary_float 应带有后缀f ,binary_double应带后缀d
定义标量变量:
- <font color="#0000ff"> v_name varchar2(10);
- v_age number(10,4);--整数部分是6位
- v_sex constant vachar(10):='woman';
- v_date Date;
- v_flag boolean not null default false;
- </font>
--如果在定义变量时没有指定初始值那么变量的初始值为null。
%TYPE:当使用%type定义变量时,它会按照数据库列或者其他变量来确定新变量的类型和长度。
- DECLARE
- v_name emp.name%TYPE;
- v_t_name v_name%TYPE;
- v_age CONSTANT NUMBER(2) := 11;
- BEGIN
- SELECT name, age INTO v_name, v_age FROM emp WHERE emp.id = '45';
- v_t_name := v_name;
- dbms_output.put_line('名字是:' || v_t_name);
- END;
复合(Composit)类型:
复合变量用来存放多个值的变量。pl/sql复合数据类型包括记录,表,嵌套表以及数组(varry);
记录:首先在定义部分定义记录类型和记录变量,然后再执行部分引用该记录变量
- DECLARE
- TYPE test_type_emp IS RECORD(
- v_name emp.name%TYPE,
- v_age emp.age%Type);
- v_r_emp test_type_emp; -- 定义一个record变量test_type_emp
- BEGIN
- SELECT name, age INTO v_r_emp FROM emp WHERE emp.id = '45';
- dbms_output.put_line('名字是:' || v_r_emp.v_name);
- END;
嵌套表(Nested Table):类似高级语言的数组,只是嵌套表的个数没有限制。嵌套表可以作为
表列的数据类型。pl/sql表不能作为表列的数据类型。
- --创建嵌套表
- REATE OR REPLACE TYPE test_emp_type AS OBJECT
- (
- name varchar2(10),
- age number
- ) ;
- CREATE OR REPLACE TYPE emp_type IS TABLE OF test_emp_type;
创建嵌套表后,就可以在表列或对象属性中将其作为用户自定义类型来引用。
值得注意的是当使用嵌套表作为表列时必须专门为其制定专门的存储表。
- CREATE TABLE hoom(
- hname varchar2(100);
- vemp emp_array
- )NESTED TABLE STORE AS vemp
数组:类似嵌套表,varray的元素是有限制的。当使用varray数组时要先建立一个varray。当建立了varry类型之后,可以在表列或对象属性中将其作为用户自定义数据类型。
- CREATE OR REPLACE TYPE test_emp_type AS OBJECT
- (
- name varchar2(10),
- age number
- )
- ;
- CREATE OR REPLACE TYPE emp_array IS VARRY(10) OF test_emp_type;
- CREATE TABLE hoom(
- hname varchar2(100);
- vemp emp_array
- )
参照变量: 用于存放数值指针的变量,使用参照变量,可以使应用程序共享相同对象包括游标(Cursor)和对象类型变量(obj_type)
- DECLARE
- TYPE c1 IS REF CURSOR;
- emp_cursor c1;
- v_name emp.name%TYPE;
- v_age emp.age%TYPE;
- BEGIN
- OPEN emp_cursor FOR
- SELECT name, age FROM emp WHERE emp.name = 'a';
- LOOP
- FETCH emp_cursor
- INTO v_name, v_age;
- EXIT WHEN emp_cursor%notfound;
- dbms_output.put_line('名字是:' || v_name);
- END LOOP;
- CLOSE emp_cursor;
- END;
1. HotSpot历史
SUN的JDK版本从1.3.1开始运用HotSpot虚拟机, 2006年底开源,主要使用C++实现,JNI接口部分用C实现。
HotSpot是较新的Java虚拟机,用来代替JIT(Just in Time),可以大大提高Java运行的性能。
Java原先是把源代码编译为字节码在虚拟机执行,这样执行速度较慢。而HotSpot将常用的部分代码编译为本地(原生,native)代码,这样显着提高了性能。
HotSpot JVM 参数可以分为规则参数(standard options)和非规则参数(non-standard options)。
规则参数相对稳定,在JDK未来的版本里不会有太大的改动。
非规则参数则有因升级JDK而改动的可能。
规则和非规则参数这里不做介绍了,网上资料很多。
2.HotSpot基础知识
HotSpot包括一个解释器和两个编译器(client 和 server,二选一的),解释与编译混合执行模式,默认启动解释执行。
编译器:java源代码被编译器编译成class文件(字节码),java字节码在运行时可以被动态编译(JIT)成本地代码(前提是解释与编译混合执行模式且虚拟机不是刚启动时)。
解释器: 解释器用来解释class文件(字节码),java是解释语言(书上这么说的)。
server启动慢,占用内存多,执行效率高,适用于服务器端应用;
client启动快,占用内存小,执行效率没有server快,默认情况下不进行动态编译,适用于桌面应用程序。
由-XX:+RewriteFrequentPairs参数控制 client模式默认关闭,server模式默认开启
在jre安装目录下的lib/i386/jvm.cfg 文件下。
java -version
Java HotSpot(TM) Client VM (build 14.3-b01, mixed mode, sharing)
mixed mode 解释与编译 混合的执行模式 默认使用这种模式
java -Xint -version
Java HotSpot(TM) Client VM (build 14.3-b01, interpreted mode, sharing)
interpreted 纯解释模式 禁用JIT编译
java -Xcomp -version
Java HotSpot(TM) Client VM (build 14.3-b01, compiled mode, sharing)
compiled 纯编译模式(如果方法无法编译,则回退到解释模式执行无法编译的方法)
3.动态编译
动态编译(compile during run-time),英文称Dynamic compilation;Just In Time也是这个意思。
HotSpot对bytecode的编译不是在程序运行前编译的,而是在程序运行过程中编译的。
HotSpot里运行着一个监视器(Profile Monitor),用来监视程序的运行状况。
java字节码(class文件)是以解释的方式被加载到虚拟机中(默认启动时解释执行)。 程序运行过程中,那一部分运用频率大,那些对程序的性能影响重要。对程序运行效率影响大的代码,称为热点(hotspot),HotSpot会把这些热点动态地编译成机器码(native code),同时对机器码进行优化,从而提高运行效率。对那些较少运行的代码,HotSpot就不会把他们编译。
HotSpot对字节码有三层处理:不编译(字节码加载到虚拟机中时的状态。也就是当虚拟机执行的时候再编译),编译(把字节码编译成本地代码。虚拟机执行的时候已经编译好了,不要再编译了),编译并优化(不但把字节码编译成本地代码,而且还进行了优化)。
至于那些程序那些不编译,那些编译,那些优化,则是由监视器(Profile Monitor)决定。
4.为什么不静态编译那?
为什么字节码在装载到虚拟机之前就编译成本地代码那?
动态编译器也在许多方面比静态编译器优越。静态编译器通常很难准确预知程序运行过程中究竟什么部分最需要优化。
函数调用都是很浪费系统时间的,因为有许多进栈出栈操作。因此有一种优化办法,就是把原来的函数调用,通过编译器的编译,改成非函数调用,把函数代码直接嵌到调用出,变成顺序执行。
面向对象的语言支持多态,静态编译无效确定程序调用哪个方法,因为多态是在程序运行中确定调用哪个方法。
Tomcat分为安装版(*.exe)和非安装版或者解压版(*.zip),个人还是比较喜欢解压版。两者虽然在功能上没有什么区别,但是在设置上还是有要注意的地方。这里只谈JVM设置要注意的地方。
安装版:windows的服务会有一个tomcat服务,当启动服务的时候Tomcat会从注册表读取JVM的参数。也就是说当在tomcat的lib文件夹下catalina.bat或者startup.bat中设置JVM参数是无效的。解决办法:设置tomcat的注册表;或者使用startup.bat启动tomcat。
解压版:当点击startup.bat时,它会读取catalina.bat中的配置,不管在startup.bat文件还是在catalina.bat文件中设置JVM参数,都会读取JVM参数。
1.在eclipse设置JVM参数
打开eclipse-窗口-首选项-Java-已安装的JRE(对在当前开发环境中运行的java程序皆生效,也就是在eclipse中运行的java程序)编辑当前使用的JRE,在缺省VM参数中输入:-Xmx128m -Xms64m -Xmn32m -Xss16m
或者在运行一个java程序的时候执行:java -Xmx128m -Xms64m -Xmn32m -Xss16m Test
Test是一个class文件
2.在Tomcat服务器上设置JVM参数
set CATALINA_OPTS=-Xmx512m -Xms512m -Xmn64m -Xss2m 或者
set JAVA_OPTS=-Xmx512m -Xms512m -Xmn64m -Xss2m
设置CATALINA_OPTS 和 JAVA_OPTS都是一个道理,在启动tomcat的时候设置参数。
两者区别是JAVA_OPTS在tomcat停止的时候也会执行这个命令。
3. 查看堆的大小
在程序中查看,返回值单位是字节,当然还有其他JVM参数可以查看。
Runtime.getRuntime().maxMemory();
或者在命令行 执行 jvisualvm 命令
或者%java_home%\bin 目录下点击jvisualvm.exe
或者在命令行执行jconsole 命令
或者%java_home%\bin 目录下点击jconsole.exe
4. java内存可以分为堆内存和非堆内存
堆和非堆:堆是给开发人员用的,是在JVM启动时创建;非堆是留给JVM自己用的,用来存放类型(类和接口)的信息。它和堆不同,运行期内GC不会释放空间。如果web app用了大量的第三方jar或者应用有太多的class文件而恰好MaxPermSize设置较小,超出了也会导致这块内存的占用过多造成溢出,或者 tomcat热部署时侯不会清理前面加载的环境,只会将context更改为新部署的,非堆存的内容就会越来越多,热部署上几次就java.lang.OutOfMemoryError: PermGen space .
---- 堆内存设置:程序可以到达的,可以操作的
-Xms 初始堆内存 默认物理内存1/64,也是最小分配堆内存。当空余堆内存小于40%时,会增加到-Xms的最大限制
-Xmx 最大堆内存分配 默认物理内存1/4,当空余堆内存大于70%时,会减小到-Xms的最小限制。
一般设置 -Xms和Xms大小相等
---- 非堆内存设置
-XX:PermSize 非堆内存的初始值,默认物理内存的1/64 ,也是最小非堆内存。
-XX:MaxPermSize 非堆内存最大值,默认物理内存的1/4,
5. 典型JVM参数设置:
java -Xmx128m -Xms128m -Xmn64m -Xss1m
-Xmx128m:设置JVM最大可用内存为128M。
-Xms128m:设置JVM最小内存为128m。此值可以设置与-Xmx相同,以避免每次垃圾回收完成后JVM重新分配内存。
-Xmn2g:设置年轻代大小为2G。整个堆大小=年轻代大小 + 年老代大小 + 持久代大小。持久代一般固定大小为64m,所以增大年轻代后,将会减小年老代大小。此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8。
-Xss128k:设置每个线程的堆栈大小。JDK5.0以后每个线程堆栈大小为1M,以前每个线程堆栈大小为256K。根据应用的线程所需内存大小进行调整。在相同物理内存下,减小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000左右。
-- Ratio 英音:['reiseu] 比率
-- Eden 伊甸
-- Survivor 幸存者
java -Xmx3550m -Xms3550m -Xss128k -XX:NewRatio=4 -XX:SurvivorRatio=4 -XX:MaxPermSize=16m -XX:MaxTenuringThreshold=0
-XX:NewRatio=4:设置年轻代(包括Eden和两个Survivor区)与年老代的比值(除去持久代)。设置为4,则年轻代与年老代所占比值为1:4,年轻代占整个堆栈的1/5
-XX:SurvivorRatio=4:设置年轻代中Eden区与Survivor区的大小比值。设置为4,则两个Survivor区与一个Eden区的比值为2:4,一个Survivor区占整个年轻代的1/6
-XX:MaxPermSize=16m:设置持久代大小为16m。
-XX:MaxTenuringThreshold=0:设置垃圾最大年龄。如果设置为0的话,则年轻代对象不经过Survivor区,直接进入年老代。
对于年老代比较多的应用,可以提高效率。如果将此值设置为一个较大值,则年轻代对象会在Survivor区进行多次复制,这样可以增加对象再年轻代的存活时间,增加在年轻代即被回收的概论。
每一首歌,就是一个故事。
每一首歌,就有一个结局。
旋律中,感受了爱情的凄与美。
歌词里,体会了人生的真于谛!
人生就是一首歌,
一首不知道结局的歌,
在歌声里,
有你,有我,有他。
在歌声中,
有欢乐,有悲伤,有梦想!
我自己印象中比较喜欢的歌,因为每首歌都有一段回忆。
众人划桨开大船
童年
天涯
水手
挪威的森林
忘忧草
一起吃苦的幸福
梦醒十分
曾经最美
难道爱一个人有错吗
今天
再回首
春天里
当爱已成为往事
兄弟
伤心太平洋
祝福
大头皮鞋
新鸳鸯蝴蝶梦
九月九的酒
吻别
星月神话
白狐
丁花香
老鼠爱大米
童话
花好月圆
浪花一朵朵
常回家看看
心太软
十年
后来
忘情水
...........
蛋疼2010神马的都是浮云
这是一个给力的一年,这是一个杯具的一年,这也是一个纠结的一年。
这一年,中国依然举办了一年一度的春节联欢晚会,只是赵本山的小品越来越像冯小刚的电影,刘谦的魔术越来越像话剧,小虎队的貌合神离也只能让人感叹时光飞逝。
这一年,加拿大举办了冬奥会,这届冬奥会的话题不是点火失败,而是周洋没有“先感谢国家”。
这一年,阿sa从未婚到离婚再到热恋,让人感慨Twins不仅长得像,连做人也是一样的套路。
这一年,中国的考古学家们在曹操墓里发现了两块头盖骨,一块曹操的,一块曹操小时侯的。
这一年,江西的一个钉子户用最无奈的方式结束了自己的生命,他的死证明不了有关部门的野蛮与无耻,只能证明汽油一点就着。
这一年,8名香港游客死在了菲律宾警察的枪口下,让香港人知道菲律宾除了有“菲佣”,还有“菲警”。
这一年,一个叫《非诚勿扰》的相亲节目将挂羊头卖狗肉诠释的淋漓尽致,坐在宝马车里哭什么的都是浮云。
这一年,局长们纷纷养成了爱写“日记”的好习惯,只是关键时刻步了陈摄影艺术家的后尘。
这一年,富-士-康的员工在工作的闲暇之余玩儿起了连环接力“跳”。
这一年,上海世博会的圆满落幕成功改写了上海解放日。
这一年,期待已久的世界杯如约而至,只是比赛的胜负好像都是“保罗”说了算。
这一年,郭德纲从一个非著名相声演员变成了一个非常著名的相声演员,又说相声又做主持,偶尔还打打人,足以说明不打人的相声演员不是好主持。
这一年,海地地震震死了8个中国人,而玉树的地震却震死了许多中国人,更可恨的是新西兰的地震居然没死人。
这一年,智利不但发生了地震还发生了矿难,33名矿工在井下呆了两三个月都没死,不但证明了大地可以“孕育”人类,更像是在提醒某些国家,矿难不一定要死人。
这一年,上海的大楼火灾让电焊工搬运工们开始考虑是否需要办个证……
这一年,嫦娥二着奔月去了,到现在还没回,国人疾呼:这眼看着就要奔三了,怎么还没二够呢?这这一年,一个卖汽车的和一个卖运动鞋的在关于谁“给力”和谁“不给力”的问题上发生了分歧从而引发了“3Q大战”,最后“狗日的腾讯”做出了“一个艰难的决定”,末了才知道这俩孙子何止是“坑爹”,简直就是“坑爹”。坑爹”,简直就是“坑爹”。
---- 待完善............................
1. java代码
- public class Test2 {
- public void testString() {
- String a = "hello";
- String b = "word";
- String c = a + b;
- System.out.println(c);
- }
- public void testString2() {
- String c = "";
- for(int i=0;i<10;i++) {
- c = c+i;
- }
- System.out.println(c);
- }
- public void testStringBuffer() {
- String a = "hello";
- String b = "word";
- StringBuffer c = new StringBuffer("");
- c.append(a).append(b);
- System.out.println(c);
- }
- public void testStringBuffer2() {
- StringBuffer c = new StringBuffer("");
- for(int i=0;i<10;i++) {
- c = c.append(i);
- }
- System.out.println(c);
- }
- }
2. 字节码
- Compiled from "Test2.java"
- public class com.java.test.Test2 extends java.lang.Object{
- public com.java.test.Test2();
- Code:
- 0: aload_0
- -- 将第一个引用类型本地变量推送至栈顶
- 1: invokespecial #9; //Method java/lang/Object."<init>":()V
- -- 调用超类构造方法,实例初始化方法,私有方法
- 4: return
- -- 从当前方法返回void
- public void testString();
- Code:
- 0: ldc #16; //String hello
- -- 将int, float或String型常量值从常量池中推送至栈顶
- 2: astore_1
- -- 将栈顶引用型数值存入第二个本地变量
- 3: ldc #18; //String word
- 5: astore_2
- 6: new #20; //class java/lang/StringBuilder
- -- 创建一个新的对象,并将其引用值压入栈顶。 #20是常量池索引(本类其它和String有关的new这个值都是一样的)。
- -- 表示常量池中第20个索引的值。class java/lang/StringBuilder,该指令占用3个字节。
- 9: dup
- -- 复制栈顶数值并将复制值压入栈顶
- --JVM新创建一个对象后,首先需要调用<init>实例初始化方法,
- --初始化完成后再会做保存到局部变量或调用方法的处理(类似代码:Object obj = new Object();),
- --这样就需要在操作数栈上保留两个相同的对象引用,所以需要dup。
- 10: aload_1
- -- 将第二个引用类型本地变量推送至栈顶
- 11: invokestatic #26; //Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String;
- -- 调用静态方法 String.valueOf
- 14: invokespecial #29; //Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
- -- 调用超类构造方法,实例初始化方法,私有方法 调用StringBuilder的init方法
- 17: aload_2
- -- 将第三个引用类型本地变量推送至栈顶
- 18: invokevirtual #33; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
- -- 调用实例方法,调用StringBuilder.append
- 21: invokevirtual #37; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
- -- 调用实例方法,调用StringBuilder.toString
- 24: astore_3
- -- 将栈顶引用型 数值存入第四个本地变量
- 25: getstatic #43; //Field java/lang/System.out:Ljava/io/PrintStream;
- -- 获取指定类的静态域,并将其值压入栈顶
- 28: aload_3
- -- 将第四个引用类型本地变量推送至栈顶
- 29: invokevirtual #48; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
- -- 调用实例方法 println PrintStream.println
- 32: return
- public void testString2();
- Code:
- 0: ldc #55; //String
- -- 将int, float或String型常量值从常量池中推送至栈顶
- 2: astore_1
- 3: iconst_0
- 4: istore_2
- 5: goto 30
- -- 无条件跳转 即跳转到 30: iload_2 这里
- 8: new #20; //class java/lang/StringBuilder
- 11: dup
- 12: aload_1
- 13: invokestatic #26; //Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String;
- 16: invokespecial #29; //Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
- 19: iload_2
- 20: invokevirtual #58; //Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
- 23: invokevirtual #37; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
- 26: astore_1
- 27: iinc 2, 1
- --将指定int型变量增加指定值(i++, i--, i+=2)
- 30: iload_2
- --将第三个int型本地变量推送至栈顶
- 31: bipush 10
- --将单字节的常量值(-128~127)推送至栈顶
- 33: if_icmplt 8
- --比较栈顶两int型数值大小,当结果小于0时跳转
- 36: getstatic #43; //Field java/lang/System.out:Ljava/io/PrintStream;
- 39: aload_1
- 40: invokevirtual #48; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
- 43: return
- public void testStringBuffer();
- Code:
- 0: ldc #16; //String hello
- 2: astore_1
- 3: ldc #18; //String word
- 5: astore_2
- 6: new #63; //class java/lang/StringBuffer
- 9: dup
- 10: ldc #55; //String
- 12: invokespecial #64; //Method java/lang/StringBuffer."<init>":(Ljava/lang/String;)V
- 15: astore_3
- 16: aload_3
- 17: aload_1
- 18: invokevirtual #67; //Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;
- 21: aload_2
- 22: invokevirtual #67; //Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;
- 25: pop
- --将栈顶数值弹出 (数值不能是long或double类型的)
- 26: getstatic #43; //Field java/lang/System.out:Ljava/io/PrintStream;
- 29: aload_3
- 30: invokevirtual #70; //Method java/io/PrintStream.println:(Ljava/lang/Object;)V
- 33: return
- public void testStringBuffer2();
- Code:
- 0: new #63; //class java/lang/StringBuffer
- 3: dup
- 4: ldc #55; //String
- 6: invokespecial #64; //Method java/lang/StringBuffer."<init>":(Ljava/lang/String;)V
- 9: astore_1
- 10: iconst_0
- 11: istore_2
- 12: goto 24
- 15: aload_1
- 16: iload_2
- 17: invokevirtual #75; //Method java/lang/StringBuffer.append:(I)Ljava/lang/StringBuffer;
- 20: astore_1
- 21: iinc 2, 1
- -- iinc index ,const
- -- The index is an unsigned byte that must be an index into the local variable array of
- -- the current frame . The const is an immediate signed byte. The local variable
- -- at index must contain an int. The value const is first sign-extended to an int, and
- -- then the local variable at index is incremented by that amount.
- 24: iload_2
- 25: bipush 10
- 27: if_icmplt 15
- 30: getstatic #43; //Field java/lang/System.out:Ljava/io/PrintStream;
- 33: aload_1
- 34: invokevirtual #70; //Method java/io/PrintStream.println:(Ljava/lang/Object;)V
- 37: return
- }
ERROR [main] session.ManagerBase - IOException while loading persisted sessions: java.io.EOFException
java.io.EOFException
at java.io.ObjectInputStream$BlockDataInputStream.peekByte(ObjectInputSt
原因:
tomcat非正常关闭,sessions没有来的及完全保存,当在重启tomcat的时候,会自动读取session的内容,session无法以正确的格式读取,所以报上面的错误。
解决方法:
清空tomcat下面的work即可,session保存在work目录下。
或者 正常关闭 使用shutdown关闭。
oracle10g的客户端导出oracle9i的数据库数据以上错误。
以前我的本机只安装了oracle数据库的客户端(9i),数据库(9i)是安装在服务器上的。
今天在我本机上装了oracle10g数据库和客户端,当使用oracle10g的客户端(Oracle - OraDb10g_home1 )导出服务器上oracle9i的数据库时,出现一些错误:
EXP-00056: 遇到 ORACLE 错误 6550
ORA-06550: 第 1 行, 第 41 列:
PLS-00302: 必须说明 'SET_NO_OUTLI
ORA-06550: 第 1 行, 第 15 列:
PL/SQL: Statement ignored
EXP-00000: 导出终止失败
原因及解决方法:
使用oracle9i的客户端导出数据。
oracle10g的客户端导出oracle10g的数据库数据就可以了。
oracle9i的客户端导出oracle10g的数据库数据也可以。
- select t.name into v_name from emp t where t.id='555';
如果emp表中没有id为555的记录,就会报ORA-01403 no data found错误。
select into 也就是隐式游标,如果查不到数据返回null的数组,也就是没有一条数据,空记录。
解决方法:
首先了解一下空记录和空值
空记录:返回一行记录,但是里面的值都为null;
空值:返回的是null,也就是没有一条记录返回;
1,用聚合函数修饰:如果查不到数据的话会返回一条空记录。
如:
- select max(t.name) into v_name from emp t where t.id='555';
2,使用显示游标处理异常即可。
在网上看到类似的一个帖子,稍微总结学习一下。字节码详解
- package com.test;
- public class Test1 {
- public void test1() {
- int a = 1;
- int b = 2;
- int c = -1;
- int d = -2;
- }
- public void test2() {
- int a = 5;
- int b = 6;
- int c = -5;
- int d = -6;
- }
- public void test3() {
- int a = 32767;
- int b = 32768;
- int c = -32768;
- int d = -32769;
- }
- public void test4() {
- int a = 65535;
- int b = 65536;
- int c = 65536;
- int d = -65535;
- int e = 65536;
- }
- }
- Compiled from "Test1.java"
- public class com.test.Test1 extends java.lang.Object{
- public com.test.Test1();
- Code:
- 0: aload_0
- 1: invokespecial #8; //Method java/lang/Object."<init>":()V
- 4: return
- public void test1();
- Code:
- 0: iconst_1
- 1: istore_1
- 2: iconst_2
- 3: istore_2
- 4: iconst_m1
- 5: istore_3
- 6: bipush -2
- 8: istore 4
- 10: return
- public void test2();
- Code:
- 0: iconst_5
- 1: istore_1
- 2: bipush 6
- 4: istore_2
- 5: bipush -5
- 7: istore_3
- 8: bipush -6
- 10: istore 4
- 12: return
- public void test3();
- Code:
- 0: sipush 32767
- 3: istore_1
- 4: ldc #22; //int 32768
- 6: istore_2
- 7: sipush -32768
- 10: istore_3
- 11: ldc #23; //int -32769
- 13: istore 4
- 15: return
- public void test4();
- Code:
- 0: ldc #25; //int 65535
- 2: istore_1
- 3: ldc #26; //int 65536
- 5: istore_2
- 6: ldc #26; //int 65536
- 8: istore_3
- 9: ldc #27; //int -65535
- 11: istore 4
- 13: ldc #26; //int 65536
- 15: istore 5
- 17: return
- }
public void test1();
Code:
0: iconst_1
// 操作数栈 压入1
1: istore_1
// 把操作数栈栈顶值弹出,存入栈帧中的局部变量第一个数组中
2: iconst_2
// 操作数栈 压入2
3: istore_2
// 把操作数栈栈顶值弹出,存入栈帧中的局部变量第二个数组中
4: iconst_m1
// 将int型-1 压入至栈顶
5: istore_3
// 把操作数栈栈顶值弹出,存入栈帧中的局部变量第三个数组中
6: bipush -2
// 将int型-2 压入至栈顶
8: istore 4
// 把操作数栈栈顶值弹出,存入栈帧中的局部变量第三个数组中
10: return
// 方法执行完成 ,返回
a,b,c和d是直接从指令获取数值,而没有进行栈中交换或进入常量池。
public void test2();
Code:
0: iconst_5
// 将int型5 压入至栈顶
1: istore_1
// 把操作数栈栈顶值弹出,存入栈帧中的局部变量第一个数组中
2: bipush 6
// 将单字节的常量值(-128~127)6压入至栈顶,直接给变量。
4: istore_2
// 把操作数栈栈顶值弹出,存入栈帧中的局部变量第二个数组中
5: bipush -5
7: istore_3
8: bipush -6
10: istore 4
12: return
JAVA虚拟机对常用常量(比如0,1,2,3,4,5)的操作直接定义成了指令,而不是传统的操作指令后带操作数。 目的是减少指令长度。没有iconst_6的指令! 而是bipush 6,就是传统的指令+操作数。
a,b,c和d是直接直接给变量
public void test3();
Code:
0: sipush 32767
// 将一个短整型常量值(-32768~32767)压入到栈顶,32767占用两个字节,sipush命令栈用
// 一个字节,所有istore_1从3开始。
3: istore_1
// 把操作数栈栈顶值弹出,存入栈帧中的局部变量第一个数组中
4: ldc #22; //int 32768
// 从常量池中找到数组索引为22的数据,并把该值压入到栈顶栈中
6: istore_2
7: sipush -32768
10: istore_3
11: ldc #23; //int -32769
13: istore 4
15: return
public void test4();
Code:
0: ldc #25; //int 65535
2: istore_1
3: ldc #26; //int 65536
// 从常量池中找到数组索引为26的数据,并把该值压入到栈顶栈中
5: istore_2
6: ldc #26; //int 65536
// 从常量池中找到数组索引为26的数据,并把该值压入到栈顶栈中
8: istore_3
9: ldc #27; //int -65535
11: istore 4
13: ldc #26; //int 65536
15: istore 5
17: return
}
对于65536,大于两个字节的,编译的时候把它放入常量池部分,而把取这个数的指令写为#26,所以说
#26是线程内部可以共享的。
好处是减少了指令代码的长度.这个值可以被多次使用。
所以对于int类型的数据,0-5 直接从指令中得到;-32768~32767 直接给变量;其它的数据放到常量池中,
该范围的数据在线程内部是可共享的。
指令码 助记符 说明
0x00 nop 什么都不做
0x01 aconst_null 将null推送至栈顶
0x02 iconst_m1 将int型-1推送至栈顶
0x03 iconst_0 将int型0推送至栈顶
0x04 iconst_1 将int型1推送至栈顶
0x05 iconst_2 将int型2推送至栈顶
0x06 iconst_3 将int型3推送至栈顶
0x07 iconst_4 将int型4推送至栈顶
0x08 iconst_5 将int型5推送至栈顶
0x09 lconst_0 将long型0推送至栈顶
0x0a lconst_1 将long型1推送至栈顶
0x0b fconst_0 将float型0推送至栈顶
0x0c fconst_1 将float型1推送至栈顶
0x0d fconst_2 将float型2推送至栈顶
0x0e dconst_0 将double型0推送至栈顶
0x0f dconst_1 将double型1推送至栈顶
0x10 bipush 将单字节的常量值(-128~127)推送至栈顶
0x11 sipush 将一个短整型常量值(-32768~32767)推送至栈顶
0x12 ldc 将int, float或String型常量值从常量池中推送至栈顶
0x13 ldc_w 将int, float或String型常量值从常量池中推送至栈顶(宽索引)
0x14 ldc2_w 将long或double型常量值从常量池中推送至栈顶(宽索引)
0x15 iload 将指定的int型本地变量推送至栈顶
0x16 lload 将指定的long型本地变量推送至栈顶
0x17 fload 将指定的float型本地变量推送至栈顶
0x18 dload 将指定的double型本地变量推送至栈顶
0x19 aload 将指定的引用类型本地变量推送至栈顶
0x1a iload_0 将第一个int型本地变量推送至栈顶
0x1b iload_1 将第二个int型本地变量推送至栈顶
0x1c iload_2 将第三个int型本地变量推送至栈顶
0x1d iload_3 将第四个int型本地变量推送至栈顶
0x1e lload_0 将第一个long型本地变量推送至栈顶
0x1f lload_1 将第二个long型本地变量推送至栈顶
0x20 lload_2 将第三个long型本地变量推送至栈顶
0x21 lload_3 将第四个long型本地变量推送至栈顶
0x22 fload_0 将第一个float型本地变量推送至栈顶
0x23 fload_1 将第二个float型本地变量推送至栈顶
0x24 fload_2 将第三个float型本地变量推送至栈顶
0x25 fload_3 将第四个float型本地变量推送至栈顶
0x26 dload_0 将第一个double型本地变量推送至栈顶
0x27 dload_1 将第二个double型本地变量推送至栈顶
0x28 dload_2 将第三个double型本地变量推送至栈顶
0x29 dload_3 将第四个double型本地变量推送至栈顶
0x2a aload_0 将第一个引用类型本地变量推送至栈顶
0x2b aload_1 将第二个引用类型本地变量推送至栈顶
0x2c aload_2 将第三个引用类型本地变量推送至栈顶
0x2d aload_3 将第四个引用类型本地变量推送至栈顶
0x2e iaload 将int型数组指定索引的值推送至栈顶
0x2f laload 将long型数组指定索引的值推送至栈顶
0x30 faload 将float型数组指定索引的值推送至栈顶
0x31 daload 将double型数组指定索引的值推送至栈顶
0x32 aaload 将引用型数组指定索引的值推送至栈顶
0x33 baload 将boolean或byte型数组指定索引的值推送至栈顶
0x34 caload 将char型数组指定索引的值推送至栈顶
0x35 saload 将short型数组指定索引的值推送至栈顶
0x36 istore 将栈顶int型数值存入指定本地变量
0x37 lstore 将栈顶long型数值存入指定本地变量
0x38 fstore 将栈顶float型数值存入指定本地变量
0x39 dstore 将栈顶double型数值存入指定本地变量
0x3a astore 将栈顶引用型数值存入指定本地变量
0x3b istore_0 将栈顶int型数值存入第一个本地变量
0x3c istore_1 将栈顶int型数值存入第二个本地变量
0x3d istore_2 将栈顶int型数值存入第三个本地变量
0x3e istore_3 将栈顶int型数值存入第四个本地变量
0x3f lstore_0 将栈顶long型数值存入第一个本地变量
0x40 lstore_1 将栈顶long型数值存入第二个本地变量
0x41 lstore_2 将栈顶long型数值存入第三个本地变量
0x42 lstore_3 将栈顶long型数值存入第四个本地变量
0x43 fstore_0 将栈顶float型数值存入第一个本地变量
0x44 fstore_1 将栈顶float型数值存入第二个本地变量
0x45 fstore_2 将栈顶float型数值存入第三个本地变量
0x46 fstore_3 将栈顶float型数值存入第四个本地变量
0x47 dstore_0 将栈顶double型数值存入第一个本地变量
0x48 dstore_1 将栈顶double型数值存入第二个本地变量
0x49 dstore_2 将栈顶double型数值存入第三个本地变量
0x4a dstore_3 将栈顶double型数值存入第四个本地变量
0x4b astore_0 将栈顶引用型数值存入第一个本地变量
0x4c astore_1 将栈顶引用型数值存入第二个本地变量
0x4d astore_2 将栈顶引用型数值存入第三个本地变量
0x4e astore_3 将栈顶引用型数值存入第四个本地变量
0x4f iastore 将栈顶int型数值存入指定数组的指定索引位置
0x50 lastore 将栈顶long型数值存入指定数组的指定索引位置
0x51 fastore 将栈顶float型数值存入指定数组的指定索引位置
0x52 dastore 将栈顶double型数值存入指定数组的指定索引位置
0x53 aastore 将栈顶引用型数值存入指定数组的指定索引位置
0x54 bastore 将栈顶boolean或byte型数值存入指定数组的指定索引位置
0x55 castore 将栈顶char型数值存入指定数组的指定索引位置
0x56 sastore 将栈顶short型数值存入指定数组的指定索引位置
0x57 pop 将栈顶数值弹出 (数值不能是long或double类型的)
0x58 pop2 将栈顶的一个(long或double类型的)或两个数值弹出(其它)
0x59 dup 复制栈顶数值并将复制值压入栈顶
0x5a dup_x1 复制栈顶数值并将两个复制值压入栈顶
0x5b dup_x2 复制栈顶数值并将三个(或两个)复制值压入栈顶
0x5c dup2 复制栈顶一个(long或double类型的)或两个(其它)数值并将复制值压入栈顶
0x5d dup2_x1 <待补充>
0x5e dup2_x2 <待补充>
0x5f swap 将栈最顶端的两个数值互换(数值不能是long或double类型的)
0x60 iadd 将栈顶两int型数值相加并将结果压入栈顶
0x61 ladd 将栈顶两long型数值相加并将结果压入栈顶
0x62 fadd 将栈顶两float型数值相加并将结果压入栈顶
0x63 dadd 将栈顶两double型数值相加并将结果压入栈顶
0x64 isub 将栈顶两int型数值相减并将结果压入栈顶
0x65 lsub 将栈顶两long型数值相减并将结果压入栈顶
0x66 fsub 将栈顶两float型数值相减并将结果压入栈顶
0x67 dsub 将栈顶两double型数值相减并将结果压入栈顶
0x68 imul 将栈顶两int型数值相乘并将结果压入栈顶
0x69 lmul 将栈顶两long型数值相乘并将结果压入栈顶
0x6a fmul 将栈顶两float型数值相乘并将结果压入栈顶
0x6b dmul 将栈顶两double型数值相乘并将结果压入栈顶
0x6c idiv 将栈顶两int型数值相除并将结果压入栈顶
0x6d ldiv 将栈顶两long型数值相除并将结果压入栈顶
0x6e fdiv 将栈顶两float型数值相除并将结果压入栈顶
0x6f ddiv 将栈顶两double型数值相除并将结果压入栈顶
0x70 irem 将栈顶两int型数值作取模运算并将结果压入栈顶
0x71 lrem 将栈顶两long型数值作取模运算并将结果压入栈顶
0x72 frem 将栈顶两float型数值作取模运算并将结果压入栈顶
0x73 drem 将栈顶两double型数值作取模运算并将结果压入栈顶
0x74 ineg 将栈顶int型数值取负并将结果压入栈顶
0x75 lneg 将栈顶long型数值取负并将结果压入栈顶
0x76 fneg 将栈顶float型数值取负并将结果压入栈顶
0x77 dneg 将栈顶double型数值取负并将结果压入栈顶
0x78 ishl 将int型数值左移位指定位数并将结果压入栈顶
0x79 lshl 将long型数值左移位指定位数并将结果压入栈顶
0x7a ishr 将int型数值右(符号)移位指定位数并将结果压入栈顶
0x7b lshr 将long型数值右(符号)移位指定位数并将结果压入栈顶
0x7c iushr 将int型数值右(无符号)移位指定位数并将结果压入栈顶
0x7d lushr 将long型数值右(无符号)移位指定位数并将结果压入栈顶
0x7e iand 将栈顶两int型数值作“按位与”并将结果压入栈顶
0x7f land 将栈顶两long型数值作“按位与”并将结果压入栈顶
0x80 ior 将栈顶两int型数值作“按位或”并将结果压入栈顶
0x81 lor 将栈顶两long型数值作“按位或”并将结果压入栈顶
0x82 ixor 将栈顶两int型数值作“按位异或”并将结果压入栈顶
0x83 lxor 将栈顶两long型数值作“按位异或”并将结果压入栈顶
0x84 iinc 将指定int型变量增加指定值(i++, i--, i+=2)
0x85 i2l 将栈顶int型数值强制转换成long型数值并将结果压入栈顶
0x86 i2f 将栈顶int型数值强制转换成float型数值并将结果压入栈顶
0x87 i2d 将栈顶int型数值强制转换成double型数值并将结果压入栈顶
0x88 l2i 将栈顶long型数值强制转换成int型数值并将结果压入栈顶
0x89 l2f 将栈顶long型数值强制转换成float型数值并将结果压入栈顶
0x8a l2d 将栈顶long型数值强制转换成double型数值并将结果压入栈顶
0x8b f2i 将栈顶float型数值强制转换成int型数值并将结果压入栈顶
0x8c f2l 将栈顶float型数值强制转换成long型数值并将结果压入栈顶
0x8d f2d 将栈顶float型数值强制转换成double型数值并将结果压入栈顶
0x8e d2i 将栈顶double型数值强制转换成int型数值并将结果压入栈顶
0x8f d2l 将栈顶double型数值强制转换成long型数值并将结果压入栈顶
0x90 d2f 将栈顶double型数值强制转换成float型数值并将结果压入栈顶
0x91 i2b 将栈顶int型数值强制转换成byte型数值并将结果压入栈顶
0x92 i2c 将栈顶int型数值强制转换成char型数值并将结果压入栈顶
0x93 i2s 将栈顶int型数值强制转换成short型数值并将结果压入栈顶
0x94 lcmp 比较栈顶两long型数值大小,并将结果(1,0,-1)压入栈顶
0x95 fcmpl 比较栈顶两float型数值大小,并将结果(1,0,-1)压入栈顶;当其中一个数值为NaN时,将-1压入栈顶
0x96 fcmpg 比较栈顶两float型数值大小,并将结果(1,0,-1)压入栈顶;当其中一个数值为NaN时,将1压入栈顶
0x97 dcmpl 比较栈顶两double型数值大小,并将结果(1,0,-1)压入栈顶;当其中一个数值为NaN时,将-1压入栈顶
0x98 dcmpg 比较栈顶两double型数值大小,并将结果(1,0,-1)压入栈顶;当其中一个数值为NaN时,将1压入栈顶
0x99 ifeq 当栈顶int型数值等于0时跳转
0x9a ifne 当栈顶int型数值不等于0时跳转
0x9b iflt 当栈顶int型数值小于0时跳转
0x9c ifge 当栈顶int型数值大于等于0时跳转
0x9d ifgt 当栈顶int型数值大于0时跳转
0x9e ifle 当栈顶int型数值小于等于0时跳转
0x9f if_icmpeq 比较栈顶两int型数值大小,当结果等于0时跳转
0xa0 if_icmpne 比较栈顶两int型数值大小,当结果不等于0时跳转
0xa1 if_icmplt 比较栈顶两int型数值大小,当结果小于0时跳转
0xa2 if_icmpge 比较栈顶两int型数值大小,当结果大于等于0时跳转
0xa3 if_icmpgt 比较栈顶两int型数值大小,当结果大于0时跳转
0xa4 if_icmple 比较栈顶两int型数值大小,当结果小于等于0时跳转
0xa5 if_acmpeq 比较栈顶两引用型数值,当结果相等时跳转
0xa6 if_acmpne 比较栈顶两引用型数值,当结果不相等时跳转
0xa7 goto 无条件跳转
0xa8 jsr 跳转至指定16位offset位置,并将jsr下一条指令地址压入栈顶
0xa9 ret 返回至本地变量指定的index的指令位置(一般与jsr, jsr_w联合使用)
0xaa tableswitch 用于switch条件跳转,case值连续(可变长度指令)
0xab lookupswitch 用于switch条件跳转,case值不连续(可变长度指令)
0xac ireturn 从当前方法返回int
0xad lreturn 从当前方法返回long
0xae freturn 从当前方法返回float
0xaf dreturn 从当前方法返回double
0xb0 areturn 从当前方法返回对象引用
0xb1 return 从当前方法返回void
0xb2 getstatic 获取指定类的静态域,并将其值压入栈顶
0xb3 putstatic 为指定的类的静态域赋值
0xb4 getfield 获取指定类的实例域,并将其值压入栈顶
0xb5 putfield 为指定的类的实例域赋值
0xb6 invokevirtual 调用实例方法
0xb7 invokespecial 调用超类构造方法,实例初始化方法,私有方法
0xb8 invokestatic 调用静态方法
0xb9 invokeinterface 调用接口方法
0xba --
0xbb new 创建一个对象,并将其引用值压入栈顶
0xbc newarray 创建一个指定原始类型(如int, float, char…)的数组,并将其引用值压入栈顶
0xbd anewarray 创建一个引用型(如类,接口,数组)的数组,并将其引用值压入栈顶
0xbe arraylength 获得数组的长度值并压入栈顶
0xbf athrow 将栈顶的异常抛出
0xc0 checkcast 检验类型转换,检验未通过将抛出ClassCastException
0xc1 instanceof 检验对象是否是指定的类的实例,如果是将1压入栈顶,否则将0压入栈顶
0xc2 monitorenter 获得对象的锁,用于同步方法或同步块
0xc3 monitorexit 释放对象的锁,用于同步方法或同步块
0xc4 wide <待补充>
0xc5 multianewarray 创建指定类型和指定维度的多维数组(执行该指令时,操作栈中必须包含各维度的长度值),并将其引用值压入栈顶
0xc6 ifnull 为null时跳转
0xc7 ifnonnull 不为null时跳转
0xc8 goto_w 无条件跳转(宽索引)
0xc9 jsr_w 跳转至指定32位offset位置,并将jsr_w下一条指令地址压入栈顶
本文来自优快云博客 http://blog.youkuaiyun.com/paulluo0739/archive/2009/12/29/5094707.aspx
基础
java源文件经过编译器编译后生成一个class文件,.class是二进制字节码文件,我们无法直接打开
查看里面的内容。但可以通过javap命令查看。
比如有一个D:coce\java\test\Test20.java文件
编译
通过javac 命令编译Test20.java文件
-- javac D:coce\java\test\Test20.java 会在 D:coce\java\test目录下生成一个Test20.class文件
如果想在D:根目录下生成Test20.class文件则执行
-- javac -d D:\ D:coce\java\test\Test20.java
执行
-- java D:coce\java\test\Test20
编译器将会直接解释执行Test20.class文件
-- java -cp D:coce\java\test Test20
编译器将会先找到D:coce\java\test文件夹;然后执行该文件夹下的Test20.class文件
字节码
D:coce\java\test>javap -c Test20
直接在控制台输出字节码文件。
D:coce\java\test>javap -c Test20 > Test20.bytecode
会在class目录下生成一个Test20.bytecode文件,用记事本打开就可以看到里面的内容了。
栈结构
每一个线程都有一个栈,栈由栈帧组成,线程每调用一个方法产生一个栈帧,栈帧由三部分组成操作数栈,局部变量数组和当前方法 所属类的 常量池的 引用( 帧数据)。
局部变量数组 存放方法的参数,一些局部变量等。大小在编译时候确定的。
操作数栈 是一个LIFO栈,两个动作组成压入和取出,也是在编译时确定的。
帧数据 ,java栈帧还包括了为了支持常量池,方法返回值和异常分发需要的数据,他们被保存在帧数据中。
源文件
- package com.test;
- public class Test1 {
- public static void main(String[] args) {
- Test1 t = new Test1();
- t.sayHelloWord();
- }
- public void sayHelloWord() {
- String h1 = new String ("HelloWord");
- System.out.println(h1);
- }
- }
字节码文件
- Compiled from "Test1.java"
- public class com.test.Test1 extends java.lang.Object{
- public com.test.Test1();
- Code:
- 0: aload_0 // 将方法第一参数的地址推入栈顶,方法第一个参数就是this
- 1: invokespecial #8; //Method java/lang/Object."<init>":()V
- 4: return
- public static void main(java.lang.String[]);
- Code:
- 0: new #1; //class com/test/Test1
- 3: dup
- 4: invokespecial #16; //Method "<init>":()V
- 7: astore_1
- 8: aload_1
- 9: invokevirtual #17; //Method sayHelloWord:()V
- 12: return
- public void sayHelloWord();
- Code:
- 0: new #23; //class java/lang/String
- 3: dup
- 4: ldc #25; //String HelloWord
- 6: invokespecial #27; //Method java/lang/String."<init>":(Ljava/lang/String;)V
- 9: astore_1
- 10: getstatic #30; //Field java/lang/System.out:Ljava/io/PrintStream;
- 13: aload_1
- 14: invokevirtual #36; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
- 17: return
- }
说明:
1. ()V 标志该方法没有参数
2. #23 代表23号常量项
3. getstatic 调用静态对象
4. invokestatic:调用静态方法 ,在加载时期就确定调用方法的入口地址。
5. invokespecial:调用特定的方法,指的是不会根据对象实例的变化而变化的方法,
譬如调用构造方法,父类的方法等 ,在加载时期就确定调用方法的入口地址。
运行时,invokespecial选择方法基于引用的类型,而不是对象所属的类。
但invokevirtual则选择当前引用的对象。
6. invokevirtual:调用虚方法,具体调用的方法与调用的对象有关,如对象本身的方法。
在运行期确定才确定调用方法的入口。
7. invokeinterface:与invokevirtual一样,只是方法是在接口中声明的,在运行期确定才确定调用方法的入口。
8. 字节码前面都有些数字,0.3.4.6....等等。为什么不是连续的啊?如new指令,指令本身占一个字节(栈帧是以字节为单位的),参数占两个字节,所以dup指令从3开始。dup指令没有参数,只有指令本身占用一个字节,所以ldc指令从4开始。
public void sayHelloWord();
Code:
// 将当前对象的this压入操作栈,对于实例方法和构造函数的局部变量表 来说第一个入口总是这个“this”。
// 因为你需要访问一些实例中的方法和变量
0: new #23; //class java/lang/String
// 在堆中分配一个Sting类型的空间,用来存放String类型的对象。这是并没有存放实际的对象,只是开辟一个空间。
// 并把指向该堆的(地址)引用压入操作数栈。
3: dup
//复制操作数栈 栈顶数据,并把复制的数据压入操作数栈。当前操作数栈对该Stirng对象有两个相同的引用。
4: ldc #25; //String HelloWord
// 将常量池中HelloWord的(地址)引用压入操作数栈顶
6: invokespecial #27; //Method java/lang/String."<init>":(Ljava/lang/String;)V
// dup指令这里有两个引用,其中的一个引用调用String的构造方法,初始化对象;另一个相同的引用指向
// 初始化对象,前一个引用出栈。
// 调用的是String(String)这一个构造器。将ldc指令所找到的"HelloWord"的内容传入到new指令所开辟在堆中的字符串对象中,
// 然后将这个对象的引用压入操作数栈顶
9: astore_1
// 弹出操作数栈顶数据存放在局部变量区的第一个位置上。此时存放的是new指令创建出的,
// 已经被初始化的String对象的地址。
10: getstatic #30; //Field java/lang/System.out:Ljava/io/PrintStream;
// 调用静态对象out
13: aload_1
//把存放在局部变量表中索引1位置的对象引用压入操作栈,
14: invokevirtual #36; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
// 调用println方法
17: return
// 指向完毕,返回
}