Sunday, December 27, 2009
ruby watir 整理
watir基于处理,继承于web document的对象提供较好的支持方法,但相对于处理windows对象较弱. 就目前从各网站收集的对象来看, 基本上,对于在ie实例对象打开的web标准对象
能够成功识别与操作,其它 的脱离当前ie窗口,打开的所有弹出框都不能正常识别。其中,弹出框对象占有异常对象识别中很大一部分。
目前碰到的弹出框, 我把他们分为三大类型:
1, alerts 警告作用,如:sorry, 当前用户没有权限操作
2, confirm 需要操作按钮, 如:你确认要删除当前记录?
3, prompt+ select +confirm 需要用户输入,操作查询或点击, 最后确认, 如:download/upload (浏览+选择文件)
下面给出,上面几种弹出框watir实现识别与操作的方法,
5.1. 弹出框基于autoit + 线程实现方式
此种方法,对于第一、二种弹出框操作较有效,
因点击某个link/button等对象,而弹出的窗口(大部分弹出框可应用此种方式来实现。)
1, 定义方法
def check_for_popups
autoit = win32ole.new('autoitx3.control')
#
# do forever - assumes popups could occur anywhere/anytime in your application.
loop do
# look for window with given title. give up after 1 second.
ret = autoit.winwait('消息 -- 网页对话框', '', 1)
#ret = winactivate("microsoft internet explorer", "")
#autoit.controlclick("microsoft internet explorer", "", "[class:button; instance:1]", 2)
puts(ret)
#
# if window found, send appropriate keystroke (e.g. {enter}, {y}, {n}).
if (ret==1) then autoit.send("{enter}") end
#
# take a rest to avoid chewing up cycles and give another thread a go.
# then resume the loop.
sleep(3)
end
end
2, 程序体代码
ie.button(:name, "signin").click_no_wait
sleep(20)
$popup = thread.new { check_for_popups } # start popup handler
at_exit { thread.kill($popup) }
5.2. call 另一ruby 文件
对于第二种弹出框,像安全警告点击, 并不通过操作与点击任何对象, 即呈现弹出窗口。
我尝试用3.1方法来实现,不成功。用下面方法替代:
1, 在watir/windowhelper.rb文件中增加方法
def push_security_alert_yes_cn
@autoit.winwait "安全警报", ""
@autoit.send "{tab}"
@autoit.send "{tab}"
@autoit.send "{space}"
end
2, 定义另一调用文件 tmp.rb
require 'watir/windowhelper'
helper = windowhelper.new
helper.push_security_alert_cn_yes
3, 在打开安全url之前,启动调用ruby文件
require 'rubygems'
require 'watir' # the watir controller
require 'win32ole'
require 'watir/windowhelper'
thread.new{system('ruby c:\tmp.rb')} #你定义tmp文件存放路径
ie = watir::ie.new
ie.goto("http://www.alipay.com.cn/ ")
5.3. 修改框架底层
此种方法,针对弹出框3.
举例一, 上传下载文件
1, 修改底层代码input_elements.rb文件,
类 filefield中, 方法set.
为了支持中文,有一句替换修改为:
system("rubyw -e \"require 'win32ole'; @autoit=win32ole.new('autoitx3.control');waitresult=@autoit.winwait ' 选择文件', '', 15; sleep 1; if waitresult == 1\" -e \"@autoit.controlsettext '选择文件', '', 'edit1', '#{setpath}'; @autoit.controlsend '选择文件', '', 'button2', '{enter}';\" -e \"end\"")
2, 定义方法
def save_file(filepath)
ai = win32ole.new("autoitx3.control")
ai.winwait("文件下载", "", 5)
ai.controlfocus("文件下载", "", "保存(&s)")
sleep 1
ai.controlclick("文件下载", "", "保存(&s)", "left")
ai.winwait("另存为", "", 5)
sleep 1
ai.controlsend("另存为", "", "edit1",filepath)
ai.controlclick("另存为", "", "保存(&s)", "left")
ai.winwait("下载完毕", "", 5)
ai.controlclick("下载完毕", "", "关闭")
end
3, 程序体代码:
ie.file_field(:name, "xlsfile").set("c:\\samenameobject.html")
#上传你所指定的文件
或
ie.span(:text, "导出excel表").click_no_wait
save_file("c:\\abc.xls")
#下载到你所指定的路径
针对弹出框3, 需要用户介入查询并操作:
举例二, 中供crm中类目与客户选择的弹出框,与第一种实现方式不同在于,
用到autoit中winactivate与controlclick方法,
require 'rubygems'
require 'watir' # the watir controller
require 'watir/windowhelper'
require 'win32ole'
def check_for_popups
autoit = win32ole.new('autoitx3.control')
loop do
ret = autoit.winwait("选择 -- 网页对话框", "", 1)
puts ret
if (ret==1) then
autoit.winactivate("[class:internet explorer_tridentdlgframe]", "")
autoit.send("{tab}")
autoit.send("{tab}")
#autoit.send("湖州")
#autoit.send("{asc 2709}")
#autoit.sendunicode("a")
#autoit.clipput ("杭")
#autoit.clipget
#autoit.tooltip "this is a tooltip杭州", 0, 0
#autoit.sleep 2000
autoit.send("{tab}")
autoit.send("{tab}")
autoit.send("{enter}")
autoit.winactivate("[class:internet explorer_tridentdlgframe]", "")
autoit.controlclick("选择 -- 网页对话框", "", "[class:internet explorer_tridentlstbox; instance:2]", 2)
autoit.send("{tab}")
autoit.send("{enter}")
autoit.send("{tab}")
autoit.send("{tab}")
autoit.send("{tab}")
autoit.send("{tab}")
autoit.send("{enter}")
end
sleep(3)
end
end
ie=watir::ie.new
ie.goto("http://10.2.6.4:5100/bin/member/signin ")
#ie.goto("http://10.2.6.4:5100/ ")
#ie.element_by_xpath("//span[contains(text(), 'main road nijmegen')]").click
ie.text_field(:name, "id").set("mhl1805")
ie.text_field(:name, "password").set("zxcvbnm")
ie.button(:name, "signin").click
ie.frame(:name, "left").link(:url, "http://10.2.6.4:5100/v3/help_cust.htm").click
ie.frame(:name, "left").link(:text, "新签中供客户").click
# start popup handler
ie.frame(:name, "right").frame(:name, "rtop").button(:value, "选择客户").click_no_wait
sleep(20)
$popup = thread.new { check_for_popups }
at_exit { thread.kill($popup) }
针对第三种popup window, 如果需要与用户交互,且需要输入中文时,若用autoit sendkey对待中文支持(但官方文档说支持中文输入, 网上有不少人和我遇到同样问题),尚没有找到有效方案,有待进一步跟进。
除上述弹出框需要特殊处理外,watir中也有一些扩展tag,或第三方控件需要特殊处理的,
像:
5.4. web html编辑器
中文站的html编辑器提供: 操作id或name操作起来较方便直接
require 'rubygems'
require 'watir' # the watir controller
require 'watir/windowhelper'
require 'win32ole'
ie=watir::ie.new
ie=watir::ie.attach(:title, /阿里助手/)
ie.text_field(:name, "_fmo.a._0.s").set("mhl1805")
ie.document.getelementbyid("_editor").contentwindow.focus()
ie.send_keys("abcsadfasfd")
但也碰到有些web页面,不提供任何id,只能用autoit方法来send tab实现
require 'rubygems'
require 'watir' # the watir controller
require 'watir/windowhelper'
require 'win32ole'
ie=watir::ie.new
#ie=watir::ie.attach(:title, /alibaba/)
ie.goto('http://us.my.alibaba.com/product/buyoffer/post_buying_lead.htm')
ie.text_field(:name, "_fmp.bu._0.su").set("mhl1805")
ie.text_field(:name, "_fmp.bu._0.su").set("中国人")
ie.checkbox(:id, "detaildesc").set
ie.checkbox(:id, "detaildesc").focus()
ie.send_keys("\t"*9)
ie.send_keys('hello worldabcdef')
5.5. 对象识别其它常用tag内置方法引用
如:ie.div , ie.span, ie.cell, ie.table方法,可以实现点击操作,取值操作等.
另外提供qtp 类似描述性编程,同类型对象统计:
ie.buttons.each { |b| puts b.to_s }
puts ie.checkboxes.length
puts ie.links.length
puts ie.buttons.length等等
对于常用的innertext属性, 在watir中已经封装到方法,可以直接引用。如:
ruby在对象识别底层,封装了innertext
实现,调用方法text即可:
如:puts ie.div(:id, "intelligentword").link(:id, index.to_s).text
最后:返回文本与源代码,用下面方法:
puts ie.text()
puts ie.html()
5.6. 识别对象正则表达式支持
当然,ruby提供强大的正则表达式支持,如:属性标识正则
ie=watir::ie.attach(:title, /alibaba/)
ruby的正则表达式以"//"作为基本框架,表达式内容位于"/"之间。表达式返回一个regexp的对象。
表达式的一般规则:
/a/匹配字符a。
/\?/匹配特殊字符?。特殊字符包括^, $, ? , ., /, \, [, ], {, }, (, ), +, *.
.匹配任意字符,例如/a./匹配ab和ac。
/[ab]c/匹配ac和bc,[]之间代表范围。例如:/[a-z]/ , /[a-za-z0-9]/。
/[^a-za-z0-9]/匹配不在该范围内的字符串。
/[\d]/代表任意数字,/[\w]/代表任意字母,数字或者_,/[\s]/代表空白字符,包括空格,tab和换行。
/[\d]/,/[\w]/,/[\s]/均为上述的否定情况。
关于正则其它用法,请参照《watir技术集锦》
5.7. 最后攻略
总之,对于对象识别与操作,要借助于上述文档中的, 灵活运用autoit, xpath与异常对象操作方法。对于watir不支持的windows控件,想到第一个方法,就是采用第三方autoit技术,来模拟键盘或鼠标操作。
对于操作web对象,watir不支持特殊tag,除了扩展其底层代码外,只能深深研究一下xpath了.
最后,再搞不定,就只能到watir group里咨询高人了,呵呵。
尚没有碰到其它更好方法。。。
一些错误
如果报错 autoitx3, it helps to make automation happen
那就在windows 下运行
regsvr32 "c:\ruby\lib\ruby\gems\1.8\gems\watir-1.6.2\lib\watir\autoitx3.dll"
unknown ole server: `autoitx3.control' (wi2oleruntimeerror)
that might mean you have not resisted the autoitx3.dll in your computer,
in current watir version, autoitx3.dll has been included in the watir folder,
so run the command in "run":
regsvr32 "c:\ruby\lib\ruby\gems\1.8\gems\watir-1.6.2\lib\watir\autoitx3.dll"
then you will not get the errors :)
learn autoitx3, please go to [url]http://www.autoitscript.com/autoit3/index.shtml[/url]
i just want to use it to handle the popup windows when writing watir scripts
Sunday, August 16, 2009
Sunday, May 3, 2009
Sogou浏览器proxy
弄清这个原理以后,我们就可以利用Sogou浏览器的这个特点来实现对其它浏览器的加速了.
其实也很简单,这里有两个实现方法:
1. 最傻瓜式:
这个方法很简单,就是打开Sogou浏览器,然后将其教育网加速功能打开。这里候,再把你想用的浏览器的代理服务器地址设为 “127.0.0.1”,把端口设为“8081”,支持https,http;(如果你不知道怎么设置代理服务器,先google吧)然后,你的浏览器速 度就飞快了。
但是,这种方法有一个很明显的缺点,就是必需得开着Sogou,虽然现在大家内存都很大,但是怎么看怎么碍眼。所以就有第二种方法了。
2. 稍微进阶式:
其实也不难,按”Win”+”R”键打开运行对话框输入“C:\Program Files\SogouExplorer\SogouExplorer.exe -proxy”(以我的系统安装的为例,具体使用时把前面的那个地址替换成你的地址即可,不过别忘了后面的-proxy);这样一来,你就不用打开 Sogou浏览器,使用它的加速进程了。
如果还觉得麻烦的话。可以在注册表的启动项里面添加一条记录,具体在: [HKEY_LOCAL_MACHINESOFTWAREMicrosoftWindowsCurrentVersionRun],然后,添加一个 string变量,名字自己取,值就是那个“C:\Program Files\SogouExplorer\SogouExplorer.exe -proxy”。然后就可以了。
Monday, April 27, 2009
CentOs J2EE开发环境配置
1,mysql的安装:
按照install-binary文件来安装,因为这个安装简单明了。在mysql根目录下。按照具体提示操
作即可。但是前提是你的英文得够行。
(1)注意:由于glibc的版本不同需要下载相应的tar.gz,看看自己的glibc版本
# rpm -qa | grep glibc
glibc-common-2.3.2-11.9
glibc-2.3.2-11.9
glibc-kernheaders-2.4-8.10
glibc-devel-2.3.2-11.9
我的机器上是2.3.2,所以我下载了mysql-5.0.67-linux-i686-glibc23.tar.gz,保存到/usr/mysql
查看机子上的mysql版本,
#rpm -qa|grep mysql
有的话
#rpm - e 你的软件包名 //删除mysql
(2)建立mysql需要的用户和组
# groupadd mysql
# useradd -g mysql mysql
(3),解压缩
# cd /usr/local
# tar -xzvf /usr/mysql/mysql-5.0.67-linux-i686-glibc23.tar.gz
# ln -s /usr/local/mysql-5.0.67-linux-i686-glibc23.tar.gz
(4)生成系统数据库
# cd /usr/local/mysql
# scripts/mysql_install_db --user=mysql (这里会出现《问题二》,不用理它)
(5)修改mysql目录权限
# chown -R root /usr/local/mysql
# chgrp -R mysql /usr/local/mysql
# chown -R mysql /usr/local/data
操作mysql数据库的用户是mysql 所以要拥有数据库目录的所有权
(6)启动mysql
# /usr/local/mysql/bin/mysqld_safe --user=mysql &
注意: 执行完这个命令后,会在/tmp产生一个mysql.sock 的文件 这是访问mysql必须的!
(7)将配置文件拷贝到/etc
# cp /usr/local/mysql/support-files/my-medium.cnf /etc/my.cnf
(8)设置MYSQL的中文问题(这一步在创建用户数据库之前完成,并要重启MYSQL)
# vi /etc/my.cnf
[client]
#password = your_password
port = 3306
socket = /tmp/mysql.sock
default-character-set=gbk//添加的内容
# Here follows entries for some specific programs
# The MySQL server
[mysqld]
port = 3306
socket = /tmp/mysql.sock
default-character-set=gbk //添加的内容
其他设定:
关于mysql的一些配置项可以在/etc/my.cnf 中更改!
设置mysql执行程序的path:
# vi /etc/profile
添加:
PATH=$PATH:/usr/local/mysql/bin
export PATH
设置mysql启动
# /usr/local/mysql/bin/mysqld_safe --user=mysql & (启动MySQL,但不能停止)
使用mysql 提供的脚本
# cp /usr/local/mysql/support-files/mysql.server /usr/local/mysql/bin
# mysql.server start 或者#service mysqld start启动mysql
# mysql.server stop #service mysqld stop停止mysql
随系统自动启动
# cp /usr/local/mysql/support-files/mysql.server /etc/init.d/mysql
# chkconfig --add mysql
修改mysql超级用户root的密码
# mysqladmin -u root -p password 123456
Enter password:
回车ok.
2,JDK的安装
(1)首先要完全删除linux自带的jdk或者低版本的jdk。我的一个笨方法是删除所有java文件目录
。可以手工删除,一个一个找。高手可以指教。然后打开终端,java -version。如果没有出现版本信息
说明,你的机子上没有java环境拉,因此你可以安装jdk拉。
(2)在/usr下打开终端,建立java文件夹
#mkdir java
#cd 你的jdk存放的位置
#chmod a+x jdk-6u7-linux-i586.bin //授权
#./jdk-6u7-linux-i586.bin //安装二进制文件,一直按回车知道出现yes/no,选择yes,开始
安装。
#mv jdk-6-7-linux-i586 /usr/java/jdk
(3)配置环境变量
打开终端,
#vi /etc/profile
在done下一行加入
export JAVA_HOME=/usr/java/jdk
export CLASSPATH=.:$JAVA_HOME/lib:$JAVA_HOME/jre/lib
export PATH=$PATH:$JAVA_HOME/bin
保存后
#source /etc/profile
#java -version
版本是你新装的表示成功拉。
3,TOMCAT的安装
tomcat的安装很容易只要加压和配置环境变量就行。
首先进入你的tomcat压缩包下,然后:
#chmod a+x apache-tomcat-6.0.18.tar.gz
#tar -zvxf apache-tomcat-6.0.18.tar.gz
#mv apache-tomcat-6.0.18.tar.gz /usr/tomcat6
配置环境变量打开vi
#vi /etc/profile
在java环境变量下加入
export CATALINA_HOME=/usr/tomcat6
export CATALINA_BASE=/usr/tomcat6
保存后
#source /etc/profile
#cd /usr/tomcat6/bin
#startup.sh
测试:
打开火狐输入地址:http://localhost:8080/
出现欢迎界面表示安装成功。
4,eclipse的安装。
把eclipse考到你linux上的一个目录。/usr/eclipse
解压eclipse
# chmod a+x eclipse-SDK-3.3.1.1-linux-gtk.tar.gz
#tar -zvxf eclipse-SDK-3.3.1.1-linux-gtk.tar.gz
5,安装myeclipse
#cd 你的myeclipse安装二进制文件
#chmod a+x MyEclipse_6_0_1GA_E3_3_1_Installer.bin
#./MyEclipse_6_0_1GA_E3_3_1_Installer.bin
像windos那样的提示安装界面。
Saturday, April 4, 2009
linux中根据端口号杀进程和启动应用两例
- kill -9 $(netstat -tlnp|grep 1099|awk '{print $7}'|awk -F '/' '{print $1}')
kill -9 $(netstat -tlnp|grep 1099|awk '{print $7}'|awk -F '/' '{print $1}')其中1099为端口号。注意:
- 得写出完整端口号,否则易错杀其他进程。
- 7是进程信息所处的列号,不同Linux可能不同,需要调整。
2.两应用程序service1和service2,service2依赖service1,即得等service1启动后才能启动,若service1应用占用一确定的tcp端口,如下脚本可实现该操作:
- echo "service1 start..."
- ./service1.sh
-
- netstat -tlnp|grep 2098
- until [ "$?" -eq "0" ]
- do
- echo "waiting service1 to startup..."
- sleep 1
- netstat -tlnp|grep 2098
- done
- echo "service1 started."
- ./service2.sh
- echo "service2 started."
Tuesday, March 31, 2009
Java Initializer(Java 初始化程序)
域的隐式初始化(Field Implicit Initialization)
Java虚拟机负责对域进行隐式初始化;隐式初始化总是在任何代码之前,因为虚拟机要保证数据的正确性,这成全了粗心的coder不必为初始化担心。
实例域(instance field)的隐式初始化:
其原理是当实例化一个类时(不论以何种方式遇到new关键字时), 虚拟机就会在相应的堆内存上为该类对象开辟一块空间,内存空间大小在编译阶段就能知道,因为类中属性为原始数据类型和引用类型,引用类型存放的地址占内存 字节数是确定的(究竟多少我也不清楚)原始数据类型所占用的内存大小是固定的。当虚拟机为对象分配完内存空间后,接着会对此块内存清零,清零的结果导致所 有的对象属性将获得一个初始值,至此,实例域的隐式初始化就完成了,初始值也可以获得。
静态域(static field)的隐式初始化
当类中定义了静态域之后,事情开始变得复杂了;这可以与类中的静态代码块(static{…})一起来说;当主程序中第一次使用虚拟机的类池中不存在的类时(不管是实例化此类的对象,还是调用类中静态方法),类加载器ClassLoader(其实是应用类加载器的一个实例)就会将此类在类池中引入,其做法就是为此类创建一个Class类对象;这样也就是在堆内存中开辟了一个空间,存放Class的一个实例,此实例中保存的是刚才ClassLoader加载的类信息;静态域作为类信息,按照上面讲的“实例域的隐式初始化”过程来初始化,接着执行静态代码块。为什么先初始化静态域,然后再执行静态代码块,可以请读者先想一想,将在此文后头进行讲解。
域的显式初始化(Field Explicit Initialization)
实例域的显式初始化
实 例域的显式初始化可以在类中定义该域时进行,也是就为该域显式地为该域赋值;也可以在类构造体中为其初始化;类构造体包括构造方法和公共构造体(很多人不 知道公共构造体的概念,其实就是在类定义体中,加上一对大括号,然后在括号中编写代码,公共构造体的作用是将构造方法中相同部分提取出来(factor out),放在一个不同的构造方法中供重用,调用不同的构造方法时,都会先执行公共构造体中的代码);那么域的显式初始化顺序可以获知了:先执行定义时初始化,再执行构造体中的初始化。
静态域的显式初始化
静态域的显式初始化是在ClassLoader将类载入虚拟机类池的时候进行的,我们也可以在静态域定义处为该域赋初值,或者在static区内为静态域赋值;当ClassLoader在堆内存区为该类创建Class对象时,对静态域首先执行隐式初始化,接着对静态域进行显式初始化,次序是先执行定义处赋值,再执行static区内赋值。
由此我们可以得出结论:其实类域的初始化是在必要时才进行的,我们可以把静态域的初始化也理解成实例域的初始化,静态域就是某个Class对象的实例属性,而static代码块是虚拟机为创建Class类对象时所执行的构造体,所以也按照1.开辟内存空间给域并清零;2.执行域定义处的赋值;3.执行构造体内的赋值;对于后两项,只有在coder写出相应的代码时才会执行。
可以查看下面一段代码来验证以上的赋值顺序,但是对于第一步,则无法与后两步结合验证。
package initializationDemo;
class Item{
public Item(){
System.out.println("Item()");
}
public Item(int id){
System.out.println("Item("+id+")");
}
}
class ItemLocker{
static Item item1=new Item();
Item item2=new Item();
static{
item1=new Item(1);
}
{
System.out.println("this is the instance field block");
}
public ItemLocker(){
item2=new Item(2);
}
}
public class MainClass {
public static void main(String[] args){
try {
Class.forName("ItemLocker");
} catch (ClassNotFoundException e) {
}
ItemLocker il=new ItemLocker();
}
}
运行结果为:
Item()
Item(1)
Item()
this is the instance field block
Item(2)
最后,局部变量只支持显式初始化。局部变量在声明定义之后可以进行显式的初始化(explicitly initialized),也可以在使用前进行赋值,但是注意java虚拟机需要保证局部变量在被使用之前必须经过初始化了,这在《Thinking in java》第四版中明确说明过,这么做是为了提醒程序员可能出现的漏洞,是一种代码安全机制。
Thursday, March 26, 2009
Shell中的grep、awk和sed的常用命令和语
Shell中的grep、awk和sed的常用命令和语法
Grep的常用命令语法
1. 双引号引用和单引号引用
在g r e p命令中输入字符串参数时,最好将其用双引号括起来。例如:“m y s t r i n g”。这样做有两个原因,一是以防被误解为 s h e l l命令,二是可以用来查找多个单词组成的字符串,例如:“jet plane”,如果不用双引号将其括起来,那么单词 p l a n e将被误认为是一个文件,查询结果将返回“文件不存在”的错误信息。
在调用变量时,也应该使用双引号,诸如: g r e p“$ M Y VA R”文件名,如果不这样,将
没有返回结果。
在调用模式匹配时,应使用单引号.[root@mypc ]# echo `grep 123 111.txt` (#注意是反单引号)
2. 常用的g r e p选项有:
-c 只输出匹配行的计数。
-i 不区分大小写(只适用于单字符)。
-h 查询多文件时不显示文件名。
-l 查询多文件时只输出包含匹配字符的文件名。
-n 显示匹配行及行号。
-s 不显示不存在或无匹配文本的错误信息。
-v 显示不包含匹配文本的所有行。
3. 特殊的——在多个文件中进行查询
$ grep "sort"*.doc ( #在当前目录下所有. d o c文件中查找字符串“s o r t”)
$ grep "sort it" * (#或在所有文件中查询单词“sort it”)
接下来的所有示例是指在单个文件中进行查询
4. 行匹配
$ grep -c "48" data.f
$ 4 (#g r e p返回数字4,意义是有4行包含字符串“4 8”。)
$ grep "48" data.f (#显示包含“4 8”字符串的4行文本)
5. 显示满足匹配模式的所有行行数:
[root@mypc oid2000]# grep -n 1234 111.txt
1:1234
3:1234ab
6. 精确匹配
[root@mypc oid2000]# grep "1234\>" 111.txt
1234
7. 查询空行,查询以某个条件开头或者结尾的行。
结合使用^和$可查询空行。使用- n参数显示实际行数
[root@mypc oid2000]# grep -n "^$" 111.txt (返回结果 2: #说明第二行是空行)
[root@mypc oid2000]# grep -n "^abc" 111.txt (#查询以abc开头的行)
[root@mypc oid2000]# grep -n "abc$" 111.txt (#查询以abc结尾的行)
8. 匹配特殊字符,查询有特殊含义的字符,诸如$ . ' " * [] ^ | \ + ? ,必须在特定字符前加\。
[root@mypc oid2000]# grep "\." 111.txt (#在111.txt中查询包含“.”的所有行)
[root@mypc oid2000]# grep "my\.conf" 111.txt (#查询有文件名my. c o n f的行)
9. 目录的查询
[root@mypc oid2000]# ls –l |grep “^d” (#如果要查询目录列表中的目录)
[root@mypc oid2000]# ls –l |grep “^d[d]” (#在一个目录中查询不包含目录的所有文件)
[root@mypc]# ls –l |grpe “^d…..x..x” (#查询其他用户和用户组成员有可执行权限的目录集合)
10.排除自身
ps -ef|grep telnet | grep -v grep (在显示的进程中抽出“telnet”进程;并丢弃ps中的grep进程)
Awk的常用命令语法
awk命令擅长格式化报文或从一个大的文本文件中抽取数据包,下面是该命令的基本语法
awk [-F filed-separator] “commands” input-file(s)
[ - F域分隔符]是可选的,a w k使用空格作为缺省的域分隔符,如果在要处理的文件中是以冒号作为分割域的(如passwd文件),则在处理的时候要这样指明 awk –F: command input-file(s)
1.1域和记录
a w k执行时,其浏览域标记为$ 1,$ 2 . . . $ n。这种方法称为域标识。使用$ 1 , $ 3表示参照第1和第3域,注意这里用逗号做域分隔。如果希望打印一个有 5个域的记录的所有域,不必指明 $ 1 , $ 2 , $ 3 , $ 4 , $ 5,可使用$ 0,意即所有域。
1.2保存a w k输出
$ awk ‘{print $0}’ input-files > out-files (#重定向保存输出)
$ awk ‘{print $0}’ input-files | tee out-files (#使用t e e命令,输出到文件的同时输出到屏幕)
1.3 常用的awk命令举例
[root@mypc /]# awk '$0 ~ /user/' /etc/passwd (#如果某域含有user就将该行打印出来)
rpc:x:32:32:Portmapper RPC user:/:/sbin/nologin
rpcuser:x:29:29:RPC Service User:/var/lib/nfs:/sbin/nologin
[root@mypc /]# awk '/user/' /etc/passwd (#同上)
[root@mypc /]# awk -F: '{if ($5 ~ /user/) print $0}' /etc/passwd (#如第五域有user则输出该行)
rpc:x:32:32:Portmapper RPC user:/:/sbin/nologin
[root@mypc /]# ifconfig | awk '/inet/{print $2}' (#从ifconfig的输出中抽取含inet的行并打印第二域)
[root@mypc /]# ifconfig | awk '/inet/{print $2}' | awk -F: '{print $2}' (#在上面的基础上再抽取,这个命令可以让你直接得到本机的ip地址)
Sed的常用命令语法
Sed是一个非交互性文本流编辑器。它编辑文件或标准输入导出的文本拷贝。
1.行的匹配
[root@mypc /]# sed -n '2p' /etc/passwd 打印出第2行
[root@mypc /]# sed -n '1,3p' /etc/passwd 打印出第1到第3行
[root@mypc /]# sed -n '$p' /etc/passwd 打印出最后一行
[root@mypc /]# sed -n '/user/'p /etc/passwd 打印出含有user的行
rpc:x:32:32:Portmapper RPC user:/:/sbin/nologin
rpcuser:x:29:29:RPC Service User:/var/lib/nfs:/sbin/nologin
[root@mypc /]# sed -n '/\$/'p /etc/passwd 打印出含有$元字符的行,$意为最后一行
2.插入文本和附加文本(插入新行)
[root@mypc /]# sed -n '/FTP/p' /etc/passwd 打印出有FTP的行
ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
[root@mypc /]# sed '/FTP/ a\ 456' /etc/passwd 在含有FTP的行后面新插入一行,内容为456
[root@mypc /]# sed '/FTP/ i\ 123' /etc/passwd在含有FTP的行前面新插入一行,内容为123
[root@mypc /]# sed '/FTP/ i\ "123"' /etc/passwd在含有FTP的行前面新插入一行,内容为"123"
[root@mypc /]# sed '5 a\ 123' /etc/passwd 在第5行后插入一新行,内容为123
[root@mypc /]# sed '5 i\ “12345”' /etc/passwd 在第5行前插入一新行,内容为“12345”
3.删除文本
[root@mypc /]# sed '1d' /etc/passwd 删除第1行
[root@mypc /]# sed '1,3d' /etc/passwd 删除第1至3行
[root@mypc /]# sed '/user/d' /etc/passwd 删除带有user的行
4. 替换文本,替换命令用替换模式替换指定模式,格式为:
[ a d d r e s s [,address]] s/ pattern-to-find /replacement-pattern/[g p w n]
[root@mypc /]# sed 's/user/USER/' /etc/passwd 将第1个user替换成USER,g表明全局替换
[root@mypc /]# sed 's/user/USER/g' /etc/passwd 将所有user替换成USER
[root@mypc /]# sed 's/user/#user/' /etc/passwd 将第1个user替换成#user,如用于屏蔽作用
[root@mypc /]# sed 's/user//' /etc/passwd 将第1个user替换成空
[root@mypc /]# sed 's/user/&11111111111111/' /etc/passwd 如果要附加或修改一个很长的字符串,可以使用( &)命令,&命令保存发现模式以便重新调用它,然后把它放在替换字符串里面,这里是把&放前面
[root@mypc /]# sed 's/user/11111111111111&/' /etc/passwd 这里是将&放后面
5. 快速一行命令
下面是一些一行命令集。([ ]表示空格,[ ]表示t a b键)
‘s / \ . $ / / g’ 删除以句点结尾行
‘-e /abcd/d’ 删除包含a b c d的行
‘s / [ ] [ ] [ ] * / [ ] / g’ 删除一个以上空格,用一个空格代替
‘s / ^ [ ] [ ] * / / g’ 删除行首空格
‘s / \ . [ ] [ ] * / [ ] / g’ 删除句点后跟两个或更多空格,代之以一个空格
‘/ ^ $ / d’ 删除空行
‘s / ^ . / / g’ 删除第一个字符
‘s /COL \ ( . . . \ ) / / g’ 删除紧跟C O L的后三个字母
‘s / ^ \ / / / g’ 从路径中删除第一个\
‘s / [ ] / [ ] / / g’ 删除所有空格并用t a b键替代
‘S / ^ [ ] / / g’ 删除行首所有t a b键
‘s / [ ] * / / g’ 删除所有t a b键
如果使用s e d对文件进行过滤,最好将问题分成几步,分步执行,且边执行边测试结果。
经验告诉我们,这是执行一个复杂任务的最有效方式。
Linux SHELL if 命令参数说明
- –b 当file存在并且是块文件时返回真
- -c 当file存在并且是字符文件时返回真
- -d 当pathname存在并且是一个目录时返回真
- -e 当pathname指定的文件或目录存在时返回真
- -f 当file存在并且是正规文件时返回真
- -g 当由pathname指定的文件或目录存在并且设置了SGID位时返回为真
- -h 当file存在并且是符号链接文件时返回真,该选项在一些老系统上无效
- -k 当由pathname指定的文件或目录存在并且设置了“粘滞”位时返回真
- -p 当file存在并且是命令管道时返回为真
- -r 当由pathname指定的文件或目录存在并且可读时返回为真
- -s 当file存在文件大小大于0时返回真
- -u 当由pathname指定的文件或目录存在并且设置了SUID位时返回真
- -w 当由pathname指定的文件或目录存在并且可执行时返回真。一个目录为了它的内容被访问必然是可执行的。
- -o 当由pathname指定的文件或目录存在并且被子当前进程的有效用户ID所指定的用户拥有时返回真。
UNIX Shell 里面比较字符写法:
- -eq 等于
- -ne 不等于
- -gt 大于
- -lt 小于
- -le 小于等于
- -ge 大于等于
- -z 空串
- = 两个字符相等
- != 两个字符不等
- -n 非空串
Tuesday, March 24, 2009
Linux进程管理
一。& 最经常被用到
这个用在一个命令的最后,可以把这个命令放到后台执行
二。ctrl + z
可以将一个正在前台执行的命令放到后台,并且暂停
三。jobs
查看当前有多少在后台运行的命令
四。fg
将后台中的命令调至前台继续运行
如果后台中有多个命令,可以用 fg %jobnumber将选中的命令调出,%jobnumber是通过jobs命令查到的后台正在执行的命令的序号(不是pid)
五。bg
将一个在后台暂停的命令,变成继续执行
如果后台中有多个命令,可以用bg %jobnumber将选中的命令调出,%jobnumber是通过jobs命令查到的后台正在执行的命令的序号(不是pid)
Linux下使用Shell命令控制任务Jobs执行
ps 列出系统中正在运行的进程;
kill 发送信号给一个或多个进程(经常用来杀死一个进程);
jobs 列出当前shell环境中已启动的任务状态,若未指定jobsid,则显示所有活动的任务状态信息;如果报告了一个任务的终止(即任务的状态被标记为Terminated),shell 从当前的shell环境已知的列表中删除任务的进程标识;
bg 将进程搬到后台运行(Background);
fg 将进程搬到前台运行(Foreground);
将job转移到后台运行
如果你经常在X图形下工作,你可能有这样的经历:通过终端命令运行一个GUI程序,GUI界面出来了,但是你的终端还停留在原地,你不能在shell中继续执行其他命令了,除非将GUI程序关掉。
为了使程序执行后终端还能继续接受命令,你可以将进程移到后台运行,使用如下命令运行程序: #假设要运行xmms
$xmms &
这样打开xmms后,终端的提示又回来了。现在xmms在后台运行着呢;但万一你运行程序时忘记使用“&”了,又不想重新执行;你可以先使用ctrl+z挂起程序,然后敲入bg命令,这样程序就在后台继续运行了。
概念:当前任务
如果后台的任务号有2个,[1],[2];如果当第一个后台任务顺利执行完毕,第二个后台任务还在执行中时,当前任务便会自动变成后台任务号码“[2]”的后台任务。所以可以得出一点,即当前任务是会变动的。当用户输入“fg”、“bg”和“stop”等命令时,如果不加任何引号,则所变动的均是当前任务。
察看jobs
使用jobs或ps命令可以察看正在执行的jobs。
jobs命令执行的结果,+表示是一个当前的作业,减号表是是一个当前作业之后的一个作业,jobs -l选项可显示所有任务的PID,jobs的状态可以是running, stopped, Terminated,但是如果任务被终止了(kill),shell 从当前的shell环境已知的列表中删除任务的进程标识;也就是说,jobs命令显示的是当前shell环境中所起的后台正在运行或者被挂起的任务信息;
进程的挂起
后台进程的挂起:
在solaris中通过stop命令执行,通过jobs命令查看job号(假设为num),然后执行stop %num;
在redhat中,不存在stop命令,可通过执行命令kill -stop PID,将进程挂起;
当要重新执行当前被挂起的任务时,通过bg %num 即可将挂起的job的状态由stopped改为running,仍在后台执行;当需要改为在前台执行时,执行命令fg %num即可;
前台进程的挂起:
ctrl+Z;
进程的终止
后台进程的终止:
方法一:
通过jobs命令查看job号(假设为num),然后执行kill %num
方法二:
通过ps命令查看job的进程号(PID,假设为pid),然后执行kill pid
前台进程的终止:
ctrl+c
kill的其他作用
kill除了可以终止进程,还能给进程发送其它信号,使用kill -l 可以察看kill支持的信号。
SIGTERM是不带参数时kill发送的信号,意思是要进程终止运行,但执行与否还得看进程是否支持。如果进程还没有终止,可以使用kill -SIGKILL pid,这是由内核来终止进程,进程不能监听这个信号。
Friday, March 20, 2009
使用find查找文件的时候怎么避开某个文件目录
比如要在/usr/sam目录下查找不在dir1子目录之内的所有文件
find /usr/sam -path "/usr/sam/dir1" -prune -o -print
find [-path ..] [expression] 在路径列表的后面的是表达式
-path "/usr/sam" -prune -o -print 是 -path "/usr/sam" -a -prune -o -print 的简写表达式按顺序求值, -a 和 -o 都是短路求值,与 shell 的 && 和 || 类似如果 -path "/usr/sam" 为真,则求值 -prune , -prune 返回真,与逻辑表达式为真;否则不求值 -prune,与逻辑表达式为假。如果 -path "/usr/sam" -a -prune 为假,则求值 -print ,-print返回真,或逻辑表达式为真;否则不求值 -print,或逻辑表达式为真。
这个表达式组合特例可以用伪码写为
if -path "/usr/sam" then
-prune
else
避开多个文件夹
find /usr/sam \( -path /usr/sam/dir1 -o -path /usr/sam/file1 \) -prune -o -print
圆括号表示表达式的结合。
\ 表示引用,即指示 shell 不对后面的字符作特殊解释,而留给 find 命令去解释其意义。
查找某一确定文件,-name等选项加在-o 之后
#find /usr/sam \(-path /usr/sam/dir1 -o -path /usr/sam/file1 \) -prune -o -name "temp" -print
Monday, March 16, 2009
linux常用的文件重定向命令
command >> filename 把标准输出重定向到一个文件中(追加)
command 1 > fielname 把标准输出重定向到一个文件中
command > filename 2>&1 把标准输出和标准错误一起重定向到一个文件中
command 2 > filename 把标准错误重定向到一个文件中
command 2 >> filename 把标准输出重定向到一个文件中(追加)
command >> filename 2>&1 把标准输出和标准错误一起重定向到一个文件中(追加)
command <>filename2 把command命令以filename文件作为标准输入,以filename2文件作为标准输出
command < filename 把command命令以filename文件作为标准输入
command << delimiter 把从标准输入中读入,直至遇到delimiter分界符
command <&m 把文件描述符m作为标准输入
command >&m 把标准输出重定向到文件描述符m中
command <&- 把关闭标准输入
Thursday, February 19, 2009
微软架构师谈编程语言发展(zz)
大约2个月前,在Herb Sutter的网站上看到了一个链接,内容是Channel9网站对他和其他三名微软架构师就“编程语言发展”进行的采访,架构师中有Anders Hejlsberg。一看之下,就被这个视频迷住了。这些大师站在历史的高度,通观全局又不失细节,高屋建瓴,有点有面地谈到了多个语言的发展和语言间的 相互关系。看完之后,感到视野得到了不小地开拓,对于语言、框架、工具的关系;对于静态(动态)类型、函数(命令)型编程;对于“可组合性”、“并发性 ”、“抽象层次”都有了更多的认识。
说开点,随着互联网的真正深入生活,随着“多核”时代的到来,IT技术领域正在经历一场变革。这场变革和“可组合性”、“并发性”这两个关键词息息相关。 围绕着这两个关键词,若干新点子,新技术被提出来,而这些新技术往往与软件产业生产者所用的工具——编程语言紧密相关。因此,作为一个软件职业者(或爱好 者),听听大师的谈话,对于把握这场变革的脉搏,跟上变革的潮流都不无裨益。看完视频,感到由于语言关系,如此好的材料无法为广大中国程序员所知,实在是 个遗憾,于是萌发了编译的念头。水平所限,错误难免,欢迎大家指正!此文在本人博客上发布后,drdirac和pongba 两位朋友对译文提出了若干补充和修正,在此表示感谢!
Charles:今天的访谈主要讨论两个相关的论题:可组合性与编程语言。作为程序员,当我们构造系统时,总是要面对这 两个问题。你们是创设语法,搭建架构的人。所以,我想讨论的一点是,你们是如何协调工作的?三个语言——C#、VB和C++都在演进,同时又服务于不同目 的,C++更多服务于系统级,C#和VB更多偏向应用层面这一切是如何形成的?你们一起工作吗?你们是如何决定语言创新的?你们是一起设计,还是想到什么 后再与他人共享?
Anders:我想,你说的两种情况都存在。早在做LINQ之前,Erik就在COmega项目上做了不少工作。LINQ和COmega相互影响,相似之 处很多。我和他一直在讨论相关问题。实际上,Erik也在C#设计组中,我们总是在交换意见。VB组和C++组的人也在一幢楼里,大家经常碰到一起。
Charles:但我的意思是,你们是否也像最终用户一样对自己做出区分?比如,有的事情在VB中能做,C#中就做不了。例如,VB以非常简单的方式实现了完全的晚绑定,而C#中根本没有晚绑定。为什么VB和C#如此不同?你们有意这样设计的吗?
Anders:我认为,影响这个问题更多的是历史原因。VB有其悠久而丰富的历史。VB刚出现时就是晚绑定语言,没有任何类型。很显然,晚绑定对VB来说 有某种核心作用。但是,从那时起,VB已逐步演进为一种更“强类型”的语言,到现在,你甚至可以把VB看作一种支持晚绑定的强类型语言。呵呵。实际的过程 刚好相反。C#从一开始就是强类型语言,而且,直到现在我们都坚持早绑定。这并非说C#未来也不会支持晚绑定,但是,它很可能以不同于VB的方式来做,而 且可能会有所改进。C#是否支持晚绑定其实只是一种选择。对于老式的弱类型对象模型来说,比如OLE,如果我们从晚绑定角度出发,会比从早绑定角度出发好 讨论得多,因为这种对象模型无非就是对象若干方法的交互,反射等。
Herb:在一定程度上,用户造成了语言之间的差异。对于靠近底层编程的C和C++程序员来说,性能永远都是一个主要问题。你可能发现不同语言有不同特 性,但是,更经常的,你会发现这些不同特性想要解决的是同一类问题,比如,“并发执行”。现在,没人能忽视这个问题。在未来5到10年,一种语言如果想留 在主流编程语言的队伍中,这个问题就无法忽视,因为这是硬件的发展方向。我们正处于一个新时代——50年来,我们首次在非单核的机器上工作。任何人都无法 忽视这点。因此,就这点来说,大家面临相似的问题。但是,根据处理方式、语法的不同,具体特性也会不尽相同。我也相信,不同语言推出相似特性的时间先后顺 序也不相同,因为不同语言服务于不同客户群体,客户要求不同。就像Anders所说,各种情况都有一些。
Erik:我给个具体例子,说明VB和C#的差异。这例子是“无名表达式(或lambda表达式)”。我们想在VB中加入这种功能。首先就是寻找正确的语 法。我们向VB项目组要到了VB的主名称表,名称表中的名字往往对VB和C#都适用。但是,这次他们想要更像关键字的名字,而不是像C#那样长长的名字, 他们觉得像关键字的名字更加“VB化”一些。这里你看到的就是语法上的区别。但在语义上也有区别。当你查看一个大函数内部嵌套很深的结构,比如for循环 时,语言是何时、如何处理变量捕获、如何进行实例保护就非常不同。在C#中,每次循环时实例都被保护,而VB有点像JavaScript,变量被隐性提升 到函数顶部。所以,在变量捕获方面也存在语义上的区别。有时,这些区别极其细微,你必须用非常变态的程序才能观察到。
Anders:只要你写出依赖这样的特性的程序,我们就能找出成百的Bug。
Brian:你逃不出“作战室”的。(译者注:微软“作战室”,是产品、程序、测试人员一起确认需求、找Bug之所在。)
Charles:这样看来,大家都同意不同语言在相互影响,不断演进。对于VB和C#来说,有相同的核心:处理引擎,你 们必须在CLR的基础上出发,随着CLR的演进而演进。很显然,C++属于另一个世界。但各种语言要互相影响,你们必须在C#中加点什么来吸引用户,让他 们用C#而不是VB.NET,是吧?应该不止是语法的区别,语言中必须还有一些核心的东西来吸引用户。
Herb:你说得对。但是,我不同意你提出的理由,说我们必须在各自的语言中加点什么特性吸引用户,从而使他们不去使用其他的微软的语言。为什么呢?比如 我更加关心使用C++或者C#的用户到底需要什么,怎样才能帮助他们把工作完成得更好。也许某种语言性能强大,但我的工作是怎样才能使客户的工作更成功? 我必须要考虑客户会如何集成,我怎样做才能使客户工作得更好,这也是CLR的核心所在,因为目前已经不是靠一种语言就能完成整个项目的时代了。我怀疑在稍 有点规模的项目中,是否还有人仅仅依靠一种开发语言。
一般说来,你用脚本语言写代码;其他语言写工具和组件;系统语言写核心——不停地在做集成。这就带来了我们所讨论的“可组合性”的问题。因为“可组合性” 本质上就是跨语言的问题。当你写Web浏览器时,你不知道某个插件是用C#、C++,某种CLR扩展,还是其他什么写的。不管如何,这些东西必须一起工 作,这就是主要的挑战。因为,要想使这种“可组合性”成为现实,我们必须时时将CLR和CLR以外的东西当作白盒来考虑。但是,这样做的时候又会碰到“锁 ”的问题。“并发执行”已经越来越重要了,但是,“锁”完全不具备可组合性。因此,这是“可组合性”面对的主要障碍。总之,对我而言,这更多的是一个语言 交互的问题,而非语言竞争的问题。
Brian:我在一定程度上代表了用户。我是个物理学家,同时,我也经常写点小程序,进行模拟和仿真,解决一些数学问题。要想成功,“可组合性”对我的来 说非常重要。我可以不在乎编程语言,但是我很在乎该语言是否有我所需要的组件。基本上,我十分愿意使用任何能使我的工作更简单的编程语言。
这里,我要戴上顶“老人”帽,谈谈历史上非常少的成功软件之一:数值计算库。这些东西是N年以前用Fortran写的。几十年以来,人们用这些库解决了许 多非常重要的科学问题。任何头脑正常的人都不会想重新写一个“线性代数包”或者类似的东西。有许多数学家终其一生在完善这些软件包。我们需要的是“互操作 性”,更是“可组合性”。所有人都知道,Fortran不支持递归,因为变量基于引用传递。这就带来了包接口的问题:如果你想要集成自身就做集成的东西, 你就不能在用这个包来集成自己,这行不通。回到C++、C#和VB上,这些语言我都使用,但更喜欢C#一些,主要因为它的操作符重载。为什么我喜欢操作符 重载?因为我做奇怪的线代计算,如四元数、八元数,此时用一个小加号就能够代表一大堆怪异的计算。
可能听上去有点像是使用模板,但绝不是这样,我一用模板就会开始想:模板的预处理器是完备的,也许我可以仅用模板就实现出一个链表处理库来解决。很快,我 就会偏离真正的数学思考。在应用程序绝对需要晚绑定的场合,比如,那些小的计算模拟器。此时,我很自然地会选择VB。至于C++,大多数时候,它被用来实 现其他的语言。在用于科学的环境下,我多次实现过Scheme。总之,就是泛谈“可组合性”。
Anders:当我开始编程生涯时,进入编程这行的学习曲线就是:学习要使用的编程语言本身。各个编程语言几乎在每个方面都不相同。语法是你要学习的很大 一部分。但这是以前的事了,现在你要学习巨大的框架,这个框架正越变越大,语法只是顶上的一颗“小樱桃”,我认为这方面确实进展很大。但是,实际上起作用 的东西是学习所有的API,学习你所基于的,越来越大的平台或者框架。如今,学习曲线的90%都耗费在这上面。掌握了这些,你就可以在C++、C#或者 VB.NET什么的之间,毫不费力地进行语言转换,将部分项目使用这种语言,部分项目使用那种,并且找出组合这些语言的解决方案。相对于以前,实际上是不 久之前,这是个主要的进步。当然,所有这些得以出现,是由于有了通用的类型系统,以及各种语言中的那些抽象。每种语言之间的差别则是细微的,而且这些差别 说不上来有什么特别的理由。
Brian:有时,这些语言必须综合运用。比如,如今的Windows编程就是一大苦事:你必须懂PHP、JavaScript、HTML、XML、 SQL等等,要把这些东西全写到名片上,你就只有小小的一块地方可以写自己的名字了。当然,能够同时使用多种语言也有好处,至少你可以选择自己喜欢的语 法。
Erik:我们的编程语言之所以有差异,还是因为这些语言没有能够统一起来,在语言下面还有若干不一致的地方,我们实际上是被强迫使用不同的东西。CLR 就不一样,基于CLR上使用相同的库,这些语言之间的排他性就要少一些,你可以选择,而非被迫使用某种特定的语言。
Brian:目前我们做得很多工作就是减少大家被迫使用某种语言这种情况。我们努力改进平台,增加更多的功能,提供更多的.NET库。
Charles:但是,C++除之外,像VB和C#这样的语言,确实绑定在某个框架上。这样的话,在一定意义上是否有局限性?如函数型程序等将如何融入到我们所谈的巨大的框架中呢?比如Haskell,又比如流行的F#,它们的结构与现在的语言完全不同。
Erik:如果我们用“命令型语言”编程,我们的基本成份是“语句”。“语句”使用并且共享“状态”,从而导致不太好的“可组合性”。你不能拿着两段语 句,然后简单地把它们粘合到一起,因为它们的全局状态不能很好地交互。这就导致“命令型语言”不能很好地组合到一起。如果你看看LINQ,就会发现我们已 经更多地采用“函数型语言”的风格,所有的东西都基于表达式。“表达式”从其定义来说就是可组合的。从一定意义上来说,我认为在C#3和VB9中没有什么 东西是Haskell或F#中没有的。这里面有一些深奥的事情,如果你看看Haskell的类型系统,你就会发现这个类型系统跟踪程序的副作用。这给了你 一定形式的可组合性。现在你虽然不能把有某种副作用的语句组合到有其他副作用的语句上,但是,你可以组合副作用相同的东西。F#有一个非常强悍的类型推断 机制,它从设计之初就考虑了类型推断。我们以前也有类型推断,这并非什么新东西,但是现在的类型推断要考虑很多困难因素,比如,重载,这些东西使类型推断 很困难。如果你从这个角度来看,我认为我们已经在很大程度上采用了浓厚的“函数型”风格,并且以相当“可组合”的方式来使用表达式和lambda表达式。
Anders:我们对“函数型编程”的兴趣并非学院式兴趣。实际上,当编程语言向前推进时,我们面临两类挑战。一是古老的追求——不断提高程序员的生产 率,对此将沿用一直以来的方法:提升抽象的层次,给程序员垃圾回收机制、类型安全、异常处理,甚至是全新的“声明型”编程语言等。在提升抽象层次的过程 中,正如Erik指出的,这些“声明型”语言获得了更高层次的“可组合型”。“函数型”语言之所以有魅力,因此你可以做出“没有副作用”,或者其他承诺, 这样一来可组合性就极大地提高了。不仅如此,在如何保证多核处理器、多CPU,比如,32个CPU始终忙碌,我们也会有所收获。显然,当我们更多地使用“ 函数型”或者“声明型”风格的编程时,我们更有可能把运行时框架构建得能更好地发挥多核的优势,更好地处理并发。如果以“命令型”风格来工作,我们能够发 挥的余地就很小,因为你无法预见所有动作——这儿拿点东西,那儿放点东西,所有动作必须串行执行,否则不可预料的事情就会发生。
Charles:作为程序员,使用了如此巨大的一个处理引擎——比如CLR之后,当然认为这些底层的东西应该被抽象掉。你的意思也是,如果我使用了一个4核的机器,运行时引擎应该有能力负责在CPU上的分配分配进程。
Anders:你这样想很正常。但是,CLR以及目前我们工业中绝大多数的运行时,都是“命令型”引擎,其指令集都相当传统,比如,堆栈增长;它们也拥有 易变的状态,包括易变的全局状态等等。在此之上,之所以能进行“函数型”编程,是因为“函数型”编程从本质上来说,是“命令型”编程所具备的能力集的一个 子集。现在我们想做的是最大化这种灵活性,但其实不过也就是让“函数型”能力子集越来越相关,使其越来越主流化而已。
Herb:我认为有必要将“函数型”编程领域划分成两个部分。我非常同意Anders和Erik的意见。我不太同意的是这样的措辞:我们之所以继续使用“ 命令型”编程语言,是因为这是大家目前所能理解的;通用程序开发者目前的工作并未取得巨大的成功;市场对于“所有的东西都是表达式,所有的语言都应该是表 达式类型的语言”这样的理念已经非常接受了;“函数型”语言是“串行执行”病的好药方。我们要想使“函数型”语言运转良好,关键点并不是处理好基本的表达 式问题,而是处理好lambda表达式和副作用的问题,是能够将表达式作为第一级的编程要素来使用——LINQ也是最近才在做,关键是能够指出 lambda表达式和Closure(译者注:函数型编程语言中的一个概念,可以方便地组合函数,返回函数)的副作用。实际上,最后这点目前是缺失的。这 些东西在“命令型”语言中也是要处理的东西。我为什么提这些?因为我觉得说“函数型”语言是方向,目前的“命令型”语言不够好,因此是垃圾,必须要抛在脑 后,全面采用“函数型”语言这样的说法不对。我认为,“函数型”语言到底能够帮助程序员完成哪些工作,目前还不太明了。比如,能够用它写通用代码吗?能够 用它系统级代码吗?当然,“函数型”语言有不少我们能够借鉴的好东西,比如lambda表达式,比如Closure,C#借鉴了,C++也在借鉴,这些语 言因此增色不少。关于“函数型”语言还有另一个问题,那就是,有两种类型的“函数型”语言。一种是没有副作用的,因此就没有共享、易变的状态的问题;一种 是人人都在使用的。因为你不太可能说,“瞧,我是完全并发安全的,因为每次我都从这个“微型像册”向量中得到一个拷贝。”或者说,“我操作这些元素的时 候,我都是取得一个拷贝”。不错,这时是没有共享、易变的状态,但是否能完全并发安全则不一定。
Anders:我的意思是,在类似C#或VB这样“命令型”编程语言中加入“函数型”结构,能给我们提供“以函数型风格”写程序库的能力,从而我们就能够 非常明确地说,如果你能保证传入的lambda表达式是纯粹的函数,我们就能保证正确地把它分散到若干个线程或者CPU上,最后再把它综合起来,给出一个 正确的结果。我们能保证代码运行得更快,同时还不用作任何编码上的修改。然而,如果你在写一个大大的For循环,我们永远都不可能保证做到前面所说的,此 时,“函数型”编程能够提供给你的是一系列表达式,再加上“把代码当作参数传递”,“类型推断和泛型编程可以正确地绑定所有的类型”等特性,这样你就能更 方便地编写“可组合的算法块”。
Charles:这不就削弱了抽象吗?
Herb:我想指出的是当前所有语言都刻意不保证 “没有副作用”。之所以如此的原因是,除非所有的语言都添加一些机制让程序员可以清除副作用,我们这些做语言的人不敢打这个包票。但是,添加这样的机制涉 及到众多参与者,大家必须一起思考、一起讨论什么是最好的方法,这个过程会很漫长。我们所做的是相信程序员,因为我们自己不知道。然而,很多情况下,程序 员也不知道,因为他写的函数要调用其他的库。而程序员根本不知道他使用的库的副作用如何。这里,“可组合性”又浮上水面了。程序员可以增加一个间接层来处 理这个问题,但是,除非他拥有涉及到的所有代码,没有人能够清楚地知道副作用会如何,问题依然存在。这就是难题所在。上面这些讨论对“锁”也适用,因为“ 锁”也是全局问题,对“可组合性”是个障碍。
Brian:在这点上Haskell做得很好,Haskell是“永远没有副作用”的范例。
Erik:是的,但做到这点的过程也很痛苦,因为并非所有情况都一目了然。一旦你的库代码有副作用,而且因此使程序员的代码必须按照某种顺序执行,在某种 意义上,你是用汇编语言在编程,因为程序员将不再能用“表达式+表达式”的方式来写代码,他必须先对某个表达式求值,再对另一表达式求值,最后把值相加。 因此,我认为我们在这点上干得还是不够漂亮。
Brian:现在,我们在“流库”上有例子。好消息是,我们已经有Haskell向你展示如何以“可行性”方面的代价,换取绝对纯粹的方式。当然,除Haskell外,我们有各种“杂牌”语言。
Anders:没有纯粹的好或坏,我认为,虽然进展缓慢,我们仍然快到一个令人满意的中间点了。我完全同意说:如果我们确实能够保证函数的纯粹性,生活将会非常美好。最终我们得做到这点。
Erik:但是,副作用也并非全然是坏事,如果函数使用了一个局部变量,这就是使用了一个状态,但是,函数本身还可以是纯粹的。我觉得很难完全避免副作用,一些东西可以是局部不纯粹而整体纯粹的。
Herb:回过头,让我们从整体上看看“可组合性”。让我吃惊的一件事是,很多时候,人们甚至都没有意识到“可组合性”是个问题,以及自己实际上经常碰到 这个问题。其实,整个软件工业乃至整个世界已经基于可组合的软件了。在硬件会议上,我经常对硬件公司提到的是:硬件的并发问题已经被仔细地探索过了,而 且,当前消除共享、易变状态的最好办法就是“锁”;但是,锁是一种全局资源,不能被组合;“被锁”是经常发生的事情,而当拥有“锁”时,我还能调用任何其 他的未知的代码;于是,“可组合性”被破坏了。说到这里,有的听者往往一脸茫然:这有什么问题吗?我于是会问,你们是否上网下载别人刚刚发布的,自己喜欢 的新软件,比如,某个浏览器然后马上使用呢?答案是肯定的。我于是会再问,你们是否意识到了,当你们这样做时,这些软件很可能都是第一次在最终用户的机器 上被组合,被使用?既然如此,你们怎么可能对其进行测试?这时,屋子里有百分之十的人会露出恍然的表情,因为此前他们没有想过这个问题:这些软件是第一次 在最终用户的机器上被组合,我们怎么进行测试?正因如此,“可组合性”是更重要的一个问题。更不用说我们现在有AJAX应用程序,以及众多的插件经常被下 载,而且被要求在同一个用户界面中协调工作。
特别感谢周爱民先生推荐此文,与广大程序员共同分享。
Friday, January 9, 2009
关键有待改进
下一个feaure是改变使的所有更soot相关的工作全部用job包装,再用rule来规定所有的soot job不能同时运行,这样应该能很好的解决soot的问题。
Saturday, January 3, 2009
Eclipse 使用技巧
Alt+左箭头,右箭头 以在编辑窗口切换标签
Alt+上下箭头, 以自动选择鼠标所在行,并将其上下移动
Ctrl+f6 可以弹出菜单,上面列出可以切换的编辑窗口,这样不用鼠标也可切换
Ctrl+f7 可以在视图之间切换 ,如编辑视图,输出视图,工程视图
Ctrl+f8 可以在不同的观察视图中切换,就是在java视图,调试视图,等之间切换
Ctrl+m 可以在最大化当前窗口和还原当前窗口之间切换
Ctrl+e 弹出输入窗口,可以输入你想要编辑的代码窗口,和Ctrl+f6的功能相同,只不过一个是选择的方式,一个是输入的方式,切换窗口
Ctrl+T 可以直接显示光标所在内容的类图,可以直接输入,并跳到输入内容部分
按住Ctrl键,然后鼠标指向变量名,方法名,类名 在源代码中快速跳转
Ctrl + F11 快速执行程序
Ctrl+Shift+F 程序代码自动排版
Ctrl+Shift+O 自动加入引用。说明: 假设我们没有Import任何类别时,当我们在程序里打入: ResourceAttirbute ra =new ResourceAttribute(); Eclipse会提示说没有引用类别,这时我们只要按下Ctrl+Shift+O ,它就会自动帮我们Import这个类别。 非常方便
Ctrl+/ 将选取的块注释起来:在Debug时很方便。
Alt + / 就是大家都应该最常用的代码辅助了
Ctrl+h 搜索,打开搜索对话框
Ctrl+Shift+Space 参数提示,如果此时位于方法体中,就会出现方法的参数提示,当前光标所在位置的参数会用粗体显示
作用域 功能 快捷键
全局 查找并替换 Ctrl+F
文本编辑器 查找上一个 Ctrl+Shift+K
文本编辑器 查找下一个 Ctrl+K
全局 撤销 Ctrl+Z
全局 复制 Ctrl+C
全局 恢复上一个选择 Alt+Shift+↓
全局 剪切 Ctrl+X
全局 快速修正 Ctrl1+1
全局 内容辅助 Alt+/
全局 全部选中 Ctrl+A
全局 删除 Delete
全局 上下文信息 Alt+?
Alt+Shift+?
Ctrl+Shift+Space
Java编辑器 显示工具提示描述 F2
Java编辑器 选择封装元素 Alt+Shift+↑
Java编辑器 选择上一个元素 Alt+Shift+←
Java编辑器 选择下一个元素 Alt+Shift+→
文本编辑器 增量查找 Ctrl+J
文本编辑器 增量逆向查找 Ctrl+Shift+J
全局 粘贴 Ctrl+V
全局 重做 Ctrl+Y
查看
作用域 功能 快捷键
全局 放大 Ctrl+=
全局 缩小 Ctrl+-
窗口
作用域 功能 快捷键
全局 激活编辑器 F12
全局 切换编辑器 Ctrl+Shift+W
全局 上一个编辑器 Ctrl+Shift+F6
全局 上一个视图 Ctrl+Shift+F7
全局 上一个透视图 Ctrl+Shift+F8
全局 下一个编辑器 Ctrl+F6
全局 下一个视图 Ctrl+F7
全局 下一个透视图 Ctrl+F8
文本编辑器 显示标尺上下文菜单 Ctrl+W
全局 显示视图菜单 Ctrl+F10
全局 显示系统菜单 Alt+-
导航
作用域 功能 快捷键
Java编辑器 打开结构 Ctrl+F3
全局 打开类型 Ctrl+Shift+T
全局 打开类型层次结构 F4
全局 打开声明 F3
全局 打开外部javadoc Shift+F2
全局 打开资源 Ctrl+Shift+R
全局 后退历史记录 Alt+←
全局 前进历史记录 Alt+→
全局 上一个 Ctrl+,
全局 下一个 Ctrl+.
Java编辑器 显示大纲 Ctrl+O
全局 在层次结构中打开类型 Ctrl+Shift+H
全局 转至匹配的括号 Ctrl+Shift+P
全局 转至上一个编辑位置 Ctrl+Q
Java编辑器 转至上一个成员 Ctrl+Shift+↑
Java编辑器 转至下一个成员 Ctrl+Shift+↓
文本编辑器 转至行 Ctrl+L
搜索
作用域 功能 快捷键
全局 出现在文件中 Ctrl+Shift+U
全局 打开搜索对话框 Ctrl+H
全局 工作区中的声明 Ctrl+G
全局 工作区中的引用 Ctrl+Shift+G
文本编辑
作用域 功能 快捷键
文本编辑器 改写切换 Insert
文本编辑器 上滚行 Ctrl+↑
文本编辑器 下滚行 Ctrl+↓
文件
作用域 功能 快捷键
全局 保存 Ctrl+X
Ctrl+S
全局 打印 Ctrl+P
全局 关闭 Ctrl+F4
全局 全部保存 Ctrl+Shift+S
全局 全部关闭 Ctrl+Shift+F4
全局 属性 Alt+Enter
全局 新建 Ctrl+N
项目
作用域 功能 快捷键
全局 全部构建 Ctrl+B
源代码
作用域 功能 快捷键
Java编辑器 格式化 Ctrl+Shift+F
Java编辑器 取消注释 Ctrl+\
Java编辑器 注释 Ctrl+/
Java编辑器 添加导入 Ctrl+Shift+M
Java编辑器 组织导入 Ctrl+Shift+O
Java编辑器 使用try/catch块来包围 未设置,太常用了,所以在这里列出,建议自己设置。
也可以使用Ctrl+1自动修正。
运行
作用域 功能 快捷键
全局 单步返回 F7
全局 单步跳过 F6
全局 单步跳入 F5
全局 单步跳入选择 Ctrl+F5
全局 调试上次启动 F11
全局 继续 F8
全局 使用过滤器单步执行 Shift+F5
全局 添加/去除断点 Ctrl+Shift+B
全局 显示 Ctrl+D
全局 运行上次启动 Ctrl+F11
全局 运行至行 Ctrl+R
全局 执行 Ctrl+U
重构
作用域 功能 快捷键
全局 撤销重构 Alt+Shift+Z
全局 抽取方法 Alt+Shift+M
全局 抽取局部变量 Alt+Shift+L
全局 内联 Alt+Shift+I
全局 移动 Alt+Shift+V
全局 重命名 Alt+Shift+R
全局 重做 Alt+Shift+Y
热键篇:Template:Alt + /修改处:窗口->喜好设定->工作台->按键->编辑->内容辅助。个人习惯:Shift+SPACE(空白)。简易说明:编辑程序代码时,打sysout +Template启动键,就会自动出现:System.out.println(); 。设定Template的格式:窗口->喜好设定->Java->编辑器->模板。程序代码自动排版:Ctrl+Shift+F 修改处:窗口->喜好设定->工作台->按键->程序代码->格式。个人习惯:Alt+Z。自动排版设定:窗口-> 喜好设定->Java->程序代码格式制作程序。样式页面->将插入tab(而非空格键)以内缩,该选项取消勾选,下面空格数目填4,这样在自动编排时会以空格4作缩排。快速执行程序:Ctrl + F11个人习惯:ALT+X修改处:窗口->喜好设定->工作台->按键->执行->启动前一次的启动作业。简易说明:第一次执行时,它会询问您执行模式,设置好后,以后只要按这个热键,它就会快速执行。
<ALT+Z(排版完)、ATL+X(执行)>..我觉得很顺手^___^自动汇入所需要的类别:Ctrl+Shift+O简易说明:假设我们没有Import任何类别时,当我们在程序里打入:
BufferedReader buf =
new BufferedReader(new InputStreamReader(System.in));
此时Eclipse会警示说没有汇入类别,这时我们只要按下Ctrl+Shift+O,它就会自动帮我们Import类别。查看使用类别的原始码:Ctrl+鼠标左键点击简易说明:可以看到您所使用类别的原始码。将选取的文字批注起来:Ctrl+/简易说明:Debug时很方便。修改处:窗口 ->喜好设定->工作台->按键->程序代码->批注视景切换:Ctrl+F8个人习惯:Alt+S。修改处:窗口 ->喜好设定->工作台->按键->窗口->下一个视景。简易说明:可以方便我们快速切换编辑、除错等视景。密技篇:一套 Eclipse可同时切换,英文、繁体、简体显示:
1.首先要先安装完中文化包。
2.在桌面的快捷方式后面加上参数即可,英文-> -nl "zh_US"繁体-> -nl "zh_TW"简体-> -nl "zh_CN"。
(其它语系以此类推)像我2.1.2中文化后,我在我桌面的Eclipse快捷方式加入参数-n1 "zh_US"。
"C:\Program Files\eclipse\eclipse.exe" -n "zh_US"接口就会变回英文语系噜。利用Eclipse,在Word编辑文书时可不必将程序代码重新编排:将Eclipse程序编辑区的程序代码整个复制下来(Ctrl+C),直接贴(Ctrl+V)到
Word或WordPad上,您将会发现在Word里的程序代码格式,跟Eclipse所设定的完全一样,包括字型、缩排、关键词颜色。我曾试过JBuilder、GEL、NetBeans...使用复制贴上时,只有缩排格式一样,字型、颜色等都不会改变。外挂篇:外挂安装:将外挂包下载回来后,将其解压缩后,您会发现features、
plugins这2个数据夹,将里面的东西都复制或移动到Eclipse的features、plugins数据夹内后,重新启动Eclipse即可。让Eclipse可以像JBuilderX一样使用拖拉方式建构GUI的外挂:
1.Jigloo SWT/Swing GUI Builder :http://cloudgarden.com/jigloo/index.html下载此版本:Jigloo plugin for Eclipse (using Java 1.4 or 1.5)安装后即可由档案->新建->其它->GUI Form选取要建构的GUI类型。
2.Eclipse Visual Editor Project:http://www.eclipse.org/vep/点选下方Download Page,再点选Latest Release 0.5.0进入下载。除了VE-runtime-0.5.0.zip要下载外,以下这2个也要:
EMF build 1.1.1: (build page) (download zip)
GEF Build 2.1.2: (build page) (download zip)
3.0 M8版本,请下载:
EMF build I200403250631
GEF Build I20040330
VE- runtime-1.0M1安装成功后,便可由File->New->Visual Class开始UI设计。安装成功后,即可由新建->Java->AWT与Swing里选择所要建构的GUI类型开始进行设计。VE必须配合着对应版本,才能正常使用,否则即使安装成功,使用上仍会有问题。使用Eclipse来开发JSP程序:外挂名称:lomboz(下载页面)http://forge.objectweb.org/project/showfiles.php?group_id=97请选择适合自己版本的 lomboz下载,lomboz.212.p1.zip表示2.1.2版,
lomboz.3m7.zip表示M7版本....以此类推。
lomboz安装以及设置教学:Eclipse开发JSP-教学文件
Java转exe篇:实现方式:Eclipse搭配JSmooth(免费)。
1.先由Eclipse制作包含Manifest的JAR。制作教学
2.使用JSmooth将做好的JAR包装成EXE。
JSmooth下载页面:http://jsmooth.sourceforge.net/index.php
3.制作完成的exe文件,可在有装置JRE的Windows上执行。
Eclipse-Java编辑器最佳设定:编辑器字型设定:工作台->字型->Java编辑器文字字型。
(建议设定Courier New -regular 10)编辑器相关设定:窗口->喜好设定->Java->编辑器外观:显示行号、强调对称显示的方括号、强调显示现行行、显示打印边距,将其勾选,Tab宽度设4,打印编距字段设80。程序代码协助:采预设即可。语法:可设定关键词、字符串等等的显示颜色。附注:采预设即可。输入:全部字段都勾选。浮动说明:采预设即可。导览:采预设即可。使自动排版排出来的效果,最符合Java设计惯例的设定:自动排版设定:窗口->喜好设定 ->Java->程序代码制作格式。换行:全部不勾选。分行:行长度上限设:80。样式:只将强制转型后插入空白勾选。内缩空格数目:设为 4。
1. Control-Shift-T: 打开类型(Open type)。如果你不是有意磨洋工,还是忘记通过源码树(source tree)打开的方式吧。
2. Control-Shift-R: 打开资源(不只是用来寻找Java文件)。小提示:利用Navigator视图的黄色双向箭头按钮让你的编辑窗口和导航器相关联。这会让你打开的文件对应显示在导航器的层级结构中,这样便于组织信息。如果这影响了速度,就关掉它。
3. F3: 打开申明(Open declaration)。或者,利用Declaration Tab(在Java视图模式下,选择Windows --> Show View -- > Declaration)。当你选中代码中的一个方法,然后按这个按键,它会把整个方法在申明方框里显示出来。
4. Alt-left arrow: 在导航历史记录(Navigation History)中后退。就像Web浏览器的后退按钮一样,在利用F3跳转之后,特别有用。(用来返回原先编译的地方)
5. Alt-right arrow: 导航历史记录中向前。
6. Control-Q: 回到最后依次编辑的地方。这个快捷键也是当你在代码中跳转后用的。特别是当你钻的过深,忘记你最初在做什么的时候。
7. Control-Shift-G: 在workspace中搜索引用(reference)。这是重构的前提。对于方法,这个热键的作用和F3恰好相反。它使你在方法的栈中,向上找出一个方法的所有调用者。一个与此相关的功能是开启“标记”功能(occurrence marking)。选择Windows->Preferences->Java-> Editor-> Mark Occurrences,勾选选项。这时,当你单击一个元素的时候,代码中所有该元素存在的地方都会被高亮显示。我个人只使用“标记本地变量”(Mark Local Variables)。注意:太多的高亮显示会拖慢Eclipse。
Code Style,然后设置Code Formatter,Code Style和Organize Imports。利用导出(Export)功能来生成配置文件。我们把这些配置文件放在wiki上,然后团队里的每个人都导入到自己的Eclipse中。JavaPreferences8. Control-Shift-F: 根据代码风格设定重新格式化代码。我们的团队有统一的代码格式,我们把它放在我们的wiki上。要这么做,我们打开Eclipse,选择Window
9. Control-O: 快速概要(quick outline)。通过这个快捷键,你可以迅速的跳到一个方法或者属性,只需要输入名字的头几个字母。
10. Control-/: 对一行注释或取消注释。对于多行也同样适用。
11. Control-Alt-down arrow: 复制高亮显示的一行或多行。
12. Alt-down arrow: 将一行或多行向下移动。Alt-up arrow会向上移动。
其他的热键在菜单里有。你可以通过按下Control-Shift-L(从3.1版本开始),看到所有快捷键的列表。按下Control-Shift-L两次,会显示热键对话框(Keys Preferences dialog),你可以在这里自己设置热键。我欢迎你在Talkback部分发表你的Eclipse提示。
Ctrl+1 快速修复(最经典的快捷键,就不用多说了)
Ctrl+D: 删除当前行
Ctrl+Alt+↓ 复制当前行到下一行(复制增加)
Ctrl+Alt+↑ 复制当前行到上一行(复制增加)
Alt+↓ 当前行和下面一行交互位置(特别实用,可以省去先剪切,再粘贴了)
Alt+↑ 当前行和上面一行交互位置(同上)
Alt+← 前一个编辑的页面
Alt+→ 下一个编辑的页面(当然是针对上面那条来说了)
Alt+Enter 显示当前选择资源(工程,or 文件 or文件)的属性
Shift+Enter 在当前行的下一行插入空行(这时鼠标可以在当前行的任一位置,不一定是最后)
Shift+Ctrl+Enter 在当前行插入空行(原理同上条)
Ctrl+Q 定位到最后编辑的地方
Ctrl+L 定位在某行 (对于程序超过100的人就有福音了)
Ctrl+M 最大化当前的Edit或View (再按则反之)
Ctrl+/ 注释当前行,再按则取消注释
Ctrl+O 快速显示 OutLine
Ctrl+T 快速显示当前类的继承结构
Ctrl+W 关闭当前Editer
Ctrl+K 参照选中的Word快速定位到下一个
Ctrl+E 快速显示当前Editer的下拉列表(如果当前页面没有显示的用黑体表示)
Ctrl+/(小键盘) 折叠当前类中的所有代码
Ctrl+×(小键盘) 展开当前类中的所有代码
Ctrl+Space 代码助手完成一些代码的插入(但一般和输入法有冲突,可以修改输入法的热键,也可以暂用Alt+/来代替)
Ctrl+Shift+E 显示管理当前打开的所有的View的管理器(可以选择关闭,激活等操作)
Ctrl+J 正向增量查找(按下Ctrl+J后,你所输入的每个字母编辑器都提供快速匹配定位到某个单词,如果没有,则在stutes line中显示没有找到了,查一个单词时,特别实用,这个功能Idea两年前就有了)
Ctrl+Shift+J 反向增量查找(和上条相同,只不过是从后往前查)
Ctrl+Shift+F4 关闭所有打开的Editer
Ctrl+Shift+X 把当前选中的文本全部变味小写
Ctrl+Shift+Y 把当前选中的文本全部变为小写
Ctrl+Shift+F 格式化当前代码
Ctrl+Shift+P 定位到对于的匹配符(譬如{}) (从前面定位后面时,光标要在匹配符里面,后面到前面,则反之)
下面的快捷键是重构里面常用的,本人就自己喜欢且常用的整理一下(注:一般重构的快捷键都是Alt+Shift开头的了)
Alt+Shift+R 重命名 (是我自己最爱用的一个了,尤其是变量和类的Rename,比手工方法能节省很多劳动力)
Alt+Shift+M 抽取方法 (这是重构里面最常用的方法之一了,尤其是对一大堆泥团代码有用)
Alt+Shift+C 修改函数结构(比较实用,有N个函数调用了这个方法,修改一次搞定)
Alt+Shift+L 抽取本地变量( 可以直接把一些魔法数字和字符串抽取成一个变量,尤其是多处调用的时候)
Alt+Shift+F 把Class中的local变量变为field变量 (比较实用的功能)
Alt+Shift+I 合并变量(可能这样说有点不妥Inline)
Alt+Shift+V 移动函数和变量(不怎么常用)
Alt+Shift+Z 重构的后悔药(Undo)
经常用到的Eclipse快捷键
存盘 Ctrl+s(一定记住)
注释代码 Ctrl+/
取消注释 Ctrl+\(Eclipse3已经都合并到Ctrl+/了)
代码辅助 Alt+/
快速修复 Ctrl+1
代码格式化 Ctrl+Shift+f
整理导入 Ctrl+Shift+o
切换窗口 Ctrl+f6<可改为ctrl+tab方便>
ctrl+shift+M 导入未引用的包
ctrl+w 关闭单个窗口
F3 跳转到类、变量的声明
F11 运行上次程序
Ctrl + F11 调试上次程序
Alt + 回下一个编辑点
ctrl+shift+T 查找工程中的类
检测Java对象所占内存大小
By Vladimir Roubtsov, JavaWorld.com, 08/16/02
Recently, I helped design a Java server application that resembled an in-memory database. That is, we biased the design toward caching tons of data in memory to provide super-fast query performance.
Once we got the prototype running, we naturally decided to profile the data memory footprint after it had been parsed and loaded from disk. The unsatisfactory initial results, however, prompted me to search for explanations.
Since Java purposefully hides many aspects of memory management, discovering how much memory your objects consume takes some work. You could use the Runtime.freeMemory() method to measure heap size differences before and after several objects have been allocated. Several articles, such as Ramchander Varadarajan's "Question of the Week No. 107" (Sun Microsystems, September 2000) and Tony Sintes's "Memory Matters" (JavaWorld, December 2001), detail that idea. Unfortunately, the former article's solution fails because the implementation employs a wrong Runtime method, while the latter article's solution has its own imperfections:
- A single call to Runtime.freeMemory() proves insufficient because a JVM may decide to increase its current heap size at any time (especially when it runs garbage collection). Unless the total heap size is already at the -Xmx maximum size, we should use Runtime.totalMemory()-Runtime.freeMemory() as the used heap size.
- Executing a single Runtime.gc() call may not prove sufficiently aggressive for requesting garbage collection. We could, for example, request object finalizers to run as well. And since Runtime.gc() is not documented to block until collection completes, it is a good idea to wait until the perceived heap size stabilizes.
- If the profiled class creates any static data as part of its per-class class initialization (including static class and field initializers), the heap memory used for the first class instance may include that data. We should ignore heap space consumed by the first class instance.
Considering those problems, I present Sizeof, a tool with which I snoop at various Java core and application classes:
- public class Sizeof
- {
- public static void main (String [] args) throws Exception
- {
- // Warm up all classes/methods we will use
- runGC ();
- usedMemory ();
- // Array to keep strong references to allocated objects
- final int count = 100000;
- Object [] objects = new Object [count];
-
- long heap1 = 0;
- // Allocate count+1 objects, discard the first one
- for (int i = -1; i <>
- {
- Object object = null;
-
- // Instantiate your data here and assign it to object
-
- object = new Object ();
- //object = new Integer (i);
- //object = new Long (i);
- //object = new String ();
- //object = new byte [128][1]
-
- if (i >= 0)
- objects [i] = object;
- else
- {
- object = null; // Discard the warm up object
- runGC ();
- heap1 = usedMemory (); // Take a before heap snapshot
- }
- }
- runGC ();
- long heap2 = usedMemory (); // Take an after heap snapshot:
-
- final int size = Math.round (((float)(heap2 - heap1))/count);
- System.out.println ("'before' heap: " + heap1 +
- ", 'after' heap: " + heap2);
- System.out.println ("heap delta: " + (heap2 - heap1) +
- ", {" + objects [0].getClass () + "} size = " + size + " bytes");
- for (int i = 0; i <>null;
- objects = null;
- }
- private static void runGC () throws Exception
- {
- // It helps to call Runtime.gc()
- // using several method calls:
- for (int r = 0; r < 4; ++ r) _runGC ();
- }
- private static void _runGC () throws Exception
- {
- long usedMem1 = usedMemory (), usedMem2 = Long.MAX_VALUE;
- for (int i = 0; (usedMem1 <>500); ++ i)
- {
- s_runtime.runFinalization ();
- s_runtime.gc ();
- Thread.currentThread ().yield ();
-
- usedMem2 = usedMem1;
- usedMem1 = usedMemory ();
- }
- }
- private static long usedMemory ()
- {
- return s_runtime.totalMemory () - s_runtime.freeMemory ();
- }
-
- private static final Runtime s_runtime = Runtime.getRuntime ();
- } // End of class
Sizeof's key methods are runGC() and usedMemory(). I use a runGC() wrapper method to call _runGC() several times because it appears to make the method more aggressive. (I am not sure why, but it's possible creating and destroying a method call-stack frame causes a change in the reachability root set and prompts the garbage collector to work harder. Moreover, consuming a large fraction of the heap space to create enough work for the garbage collector to kick in also helps. In general, it is hard to ensure everything is collected. The exact details depend on the JVM and garbage collection algorithm.)
Note carefully the places where I invoke runGC(). You can edit the code between the heap1 and heap2 declarations to instantiate anything of interest.
Also note how Sizeof prints the object size: the transitive closure of data required by all count class instances, divided by count. For most classes, the result will be memory consumed by a single class instance, including all of its owned fields. That memory footprint value differs from data provided by many commercial profilers that report shallow memory footprints (for example, if an object has an int[] field, its memory consumption will appear separately).
The results
Let's apply this simple tool to a few classes, then see if the results match our expectations.
Note: The following results are based on Sun's JDK 1.3.1 for Windows. Due to what is and is not guaranteed by the Java language and JVM specifications, you cannot apply these specific results to other platforms or other Java implementations.
java.lang.Object
Well, the root of all objects just had to be my first case. For java.lang.Object, I get:
So, a plain Object takes 8 bytes; of course, no one should expect the size to be 0, as every instance must carry around fields that support base operations like equals(), hashCode(), wait()/notify(), and so on.
java.lang.Integer
My colleagues and I frequently wrap native ints into Integer instances so we can store them in Java collections. How much does it cost us in memory?
The 16-byte result is a little worse than I expected because an int value can fit into just 4 extra bytes. Using an Integer costs me a 300 percent memory overhead compared to when I can store the value as a primitive type.
java.lang.Long
Long should take more memory than Integer, but it does not:
Clearly, actual object size on the heap is subject to low-level memory alignment done by a particular JVM implementation for a particular CPU type. It looks like a Long is 8 bytes of Object overhead, plus 8 bytes more for the actual long value. In contrast, Integer had an unused 4-byte hole, most likely because the JVM I use forces object alignment on an 8-byte word boundary.
Arrays
Playing with primitive type arrays proves instructive, partly to discover any hidden overhead and partly to justify another popular trick: wrapping primitive values in a size-1 array to use them as objects. By modifying Sizeof.main() to have a loop that increments the created array length on every iteration, I get for int arrays:
- length: 0, {class [I} size = 16 bytes
- length: 1, {class [I} size = 16 bytes
- length: 2, {class [I} size = 24 bytes
- length: 3, {class [I} size = 24 bytes
- length: 4, {class [I} size = 32 bytes
- length: 5, {class [I} size = 32 bytes
- length: 6, {class [I} size = 40 bytes
- length: 7, {class [I} size = 40 bytes
- length: 8, {class [I} size = 48 bytes
- length: 9, {class [I} size = 48 bytes
- length: 10, {class [I} size = 56 bytes
length: 0, {class [I} size = 16 bytes length: 1, {class [I} size = 16 bytes length: 2, {class [I} size = 24 bytes length: 3, {class [I} size = 24 bytes length: 4, {class [I} size = 32 bytes length: 5, {class [I} size = 32 bytes length: 6, {class [I} size = 40 bytes length: 7, {class [I} size = 40 bytes length: 8, {class [I} size = 48 bytes length: 9, {class [I} size = 48 bytes length: 10, {class [I} size = 56 bytesand for char arrays:
- length: 0, {class [C} size = 16 bytes
- length: 1, {class [C} size = 16 bytes
- length: 2, {class [C} size = 16 bytes
- length: 3, {class [C} size = 24 bytes
- length: 4, {class [C} size = 24 bytes
- length: 5, {class [C} size = 24 bytes
- length: 6, {class [C} size = 24 bytes
- length: 7, {class [C} size = 32 bytes
- length: 8, {class [C} size = 32 bytes
- length: 9, {class [C} size = 32 bytes
- length: 10, {class [C} size = 32 bytes
length: 0, {class [C} size = 16 bytes length: 1, {class [C} size = 16 bytes length: 2, {class [C} size = 16 bytes length: 3, {class [C} size = 24 bytes length: 4, {class [C} size = 24 bytes length: 5, {class [C} size = 24 bytes length: 6, {class [C} size = 24 bytes length: 7, {class [C} size = 32 bytes length: 8, {class [C} size = 32 bytes length: 9, {class [C} size = 32 bytes length: 10, {class [C} size = 32 bytesAbove, the evidence of 8-byte alignment pops up again. Also, in addition to the inevitable Object 8-byte overhead, a primitive array adds another 8 bytes (out of which at least 4 bytes support the length field). And using int[1] appears to not offer any memory advantages over an Integer instance, except maybe as a mutable version of the same data.
Multidimensional arrays
Multidimensional arrays offer another surprise. Developers commonly employ constructs like int[dim1][dim2] in numerical and scientific computing. In an int[dim1][dim2] array instance, every nested int[dim2] array is an Object in its own right. Each adds the usual 16-byte array overhead. When I don't need a triangular or ragged array, that represents pure overhead. The impact grows when array dimensions greatly differ. For example, a int[128][2] instance takes 3,600 bytes. Compared to the 1,040 bytes an int[256] instance uses (which has the same capacity), 3,600 bytes represent a 246 percent overhead. In the extreme case of byte[256][1], the overhead factor is almost 19! Compare that to the C/C++ situation in which the same syntax does not add any storage overhead.
java.lang.String
Let's try an empty String, first constructed as new String():
- 'before' heap: 510696, 'after' heap: 4510696
- heap delta: 4000000, {class java.lang.String} size = 40 bytes
'before' heap: 510696, 'after' heap: 4510696 heap delta: 4000000, {class java.lang.String} size = 40 bytesThe result proves quite depressing. An empty String takes 40 bytes—enough memory to fit 20 Java characters.
Before I try Strings with content, I need a helper method to create Strings guaranteed not to get interned. Merely using literals as in:
- object = "string with 20 chars";
object = "string with 20 chars";
will not work because all such object handles will end up pointing to the same String instance. The language specification dictates such behavior (see also the java.lang.String.intern() method). Therefore, to continue our memory snooping, try:
- public static String createString (final int length)
- {
- char [] result = new char [length];
- for (int i = 0; i <>char) i;
-
- return new String (result);
- }
public static String createString (final int length) { char [] result = new char [length]; for (int i = 0; i <>
After arming myself with this String creator method, I get the following results:
Java代码- length: 0, {class java.lang.String} size = 40 bytes
- length: 1, {class java.lang.String} size = 40 bytes
- length: 2, {class java.lang.String} size = 40 bytes
- length: 3, {class java.lang.String} size = 48 bytes
- length: 4, {class java.lang.String} size = 48 bytes
- length: 5, {class java.lang.String} size = 48 bytes
- length: 6, {class java.lang.String} size = 48 bytes
- length: 7, {class java.lang.String} size = 56 bytes
- length: 8, {class java.lang.String} size = 56 bytes
- length: 9, {class java.lang.String} size = 56 bytes
- length: 10, {class java.lang.String} size = 56 bytes
length: 0, {class java.lang.String} size = 40 bytes length: 1, {class java.lang.String} size = 40 bytes length: 2, {class java.lang.String} size = 40 bytes length: 3, {class java.lang.String} size = 48 bytes length: 4, {class java.lang.String} size = 48 bytes length: 5, {class java.lang.String} size = 48 bytes length: 6, {class java.lang.String} size = 48 bytes length: 7, {class java.lang.String} size = 56 bytes length: 8, {class java.lang.String} size = 56 bytes length: 9, {class java.lang.String} size = 56 bytes length: 10, {class java.lang.String} size = 56 bytes
The results clearly show that a String's memory growth tracks its internal char array's growth. However, the String class adds another 24 bytes of overhead. For a nonempty String of size 10 characters or less, the added overhead cost relative to useful payload (2 bytes for each char plus 4 bytes for the length), ranges from 100 to 400 percent.
Of course, the penalty depends on your application's data distribution. Somehow I suspected that 10 characters represents the typical String length for a variety of applications. To get a concrete data point, I instrumented the SwingSet2 demo (by modifying the String class implementation directly) that came with JDK 1.3.x to track the lengths of the Strings it creates. After a few minutes playing with the demo, a data dump showed that about 180,000 Strings were instantiated. Sorting them into size buckets confirmed my expectations:
Java代码- [0-10]: 96481
- [10-20]: 27279
- [20-30]: 31949
- [30-40]: 7917
- [40-50]: 7344
- [50-60]: 3545
- [60-70]: 1581
- [70-80]: 1247
- [80-90]: 874
- ...
[0-10]: 96481 [10-20]: 27279 [20-30]: 31949 [30-40]: 7917 [40-50]: 7344 [50-60]: 3545 [60-70]: 1581 [70-80]: 1247 [80-90]: 874 ...
That's right, more than 50 percent of all String lengths fell into the 0-10 bucket, the very hot spot of String class inefficiency!
In reality, Strings can consume even more memory than their lengths suggest: Strings generated out of StringBuffers (either explicitly or via the '+' concatenation operator) likely have char arrays with lengths larger than the reported String lengths because StringBuffers typically start with a capacity of 16, then double it on append() operations. So, for example, createString(1) + ' ' ends up with a char array of size 16, not 2.
What do we do?
"This is all very well, but we don't have any choice but to use Strings and other types provided by Java, do we?" I hear you ask. Let's find out.
Wrapper classes
Wrapper classes like java.lang.Integer seem a bad choice for storing large data amounts in memory. If you strive to be memory-economic, avoid them altogether. Rolling your own vector class for primitive ints isn't difficult. Of course, it would be great if the Java core API already contained such libraries. Perhaps the situation will improve when Java has generic types.
Multidimensional arrays
For large data structures built with multidimensional arrays, you can oftentimes reduce the extra dimension overhead by an easy indexing change: convert every int[dim1][dim2] instance to an int[dim1*dim2] instance and change all expressions like a[i][j] to a[i*dim1 + j]. Of course, you pay a price from the lack of index-range checking on dim1 dimension (which also boosts performance).
java.lang.String
You can try a few simple tricks to reduce your application's String static memory size.
First, you can try one common technique when an application loads and caches many Strings from a data file or a network connection, and the String value range proves limited. For example, if you want to parse an XML file in which you frequently encounter a certain attribute, but the attribute is limited to just two possible values. Your goal: filter all Strings through a hash map and reduce all equal but distinct Strings to identical object references:
Java代码- public String internString (String s)
- {
- if (s == null) return null;
-
- String is = (String) m_strings.get (s);
- if (is != null)
- return is;
- else
- {
- m_strings.put (s, s);
- return s;
- }
- }
-
- private Map m_strings = new HashMap ();
public String internString (String s) { if (s == null) return null; String is = (String) m_strings.get (s); if (is != null) return is; else { m_strings.put (s, s); return s; } } private Map m_strings = new HashMap ();
When applicable, that trick can decrease your static memory requirements by hundreds of percent. An experienced reader may observe that the trick duplicates java.lang.String.intern()'s functionality. Numerous reasons exist to avoid the String.intern() method. One is that few modern JVMs can intern large amounts of data.
What if your Strings are all different? For the second trick, recollect that for small Strings the underlying char array takes half the memory occupied by the String that wraps it. Thus, when my application caches many distinct String values, I can just keep the arrays in memory and convert them to Strings as needed. That works well if each such String then serves as a transient, quickly discarded object. A simple experiment with caching 90,000 words taken from a sample dictionary file shows that this data takes about 5.6 MB in String form and only 3.4 MB in char[] form, a 65 percent reduction.
The second trick contains one obvious disadvantage: you cannot convert a char[] back to a String through a constructor that would take ownership of the array without cloning it. Why? Because the entire public String API ensures that every String is immutable, so every String constructor defensively clones input data passed through its parameters.
Still, you can try a third trick when the cost of converting from char arrays to Strings proves too high. The trick exploits java.lang.String.substr()'s ability to avoid data copying: the method implementation exploits String immutability and creates a shallow String object that shares the char content array with the original String but has its internal start and end indices adjusted correspondingly. To make an example, new String("smiles").substring(1,5) is a String configured to start at index 1 and end at index 4 within a char buffer "smiles" shared by reference with the originally constructed String. You can exploit that fact as follows: given a large String set, you can merge its char content into one large char array, create a String out of it, and recreate the original Strings as subStrings of this master String, as the following method illustrates:
Java代码- public static String [] compactStrings (String [] strings)
- {
- String [] result = new String [strings.length];
- int offset = 0;
-
- for (int i = 0; i <>
- offset += strings [i].length ();
-
- // Can't use StringBuffer due to how it manages capacity
- char [] allchars = new char [offset];
-
- offset = 0;
- for (int i = 0; i <>
- {
- strings [i].getChars (0, strings [i].length (), allchars, offset);
- offset += strings [i].length ();
- }
-
- String allstrings = new String (allchars);
-
- offset = 0;
- for (int i = 0; i <>
- result [i] = allstrings.substring (offset,
- offset += strings [i].length ());
-
- return result;
- }
public static String [] compactStrings (String [] strings) { String [] result = new String [strings.length]; int offset = 0; for (int i = 0; i < allchars =" new" offset =" 0;" i =" 0;" allstrings =" new" offset =" 0;" i =" 0;">
The above method returns a new set of Strings equivalent to the input set but more compact in memory. Recollect from earlier measurements that every char[] adds 16 bytes of overhead; effectively removed by this method. The savings could be significant when cached data comprises mostly short Strings. When you apply this trick to the same 90,000-word dictionary mentioned above, the memory size drops from 5.6 MB to 4.2 MB, a 30 percent reduction. (An astute reader will observe in that particular example the Strings tend to share many prefixes and the compactString() method could be further optimized to reduce the merged char array's size.)
As a side effect, compactString() also removes StringBuffer-related inefficiencies mentioned earlier.
Is it worth the effort?
To many, the techniques I presented may seem like micro-optimizations not worth the time it takes to implement them. However, remember the applications I had in mind: server-side applications that cache massive amounts of data in memory to achieve performance impossible when data comes from a disk or database. Several hundred megabytes of cached data represents a noticeable fraction of maximum heap sizes of today's 32-bit JVMs. Shaving 30 percent or more off is nothing to scoff at; it could push an application's scalability limits quite noticeably. Of course, these tricks cannot substitute for beginning with well-designed data structures and profiling your application to determine its actual hot spots. In any case, you're now more aware of how much memory your objects consume.
Author Bio
Vladimir Roubtsov has programmed in a variety of languages for more than 12 years, including Java since 1995. Currently, he develops enterprise software as a senior developer for Trilogy in Austin, Texas. When coding for fun, Vladimir develops software tools based on Java byte code or source code instrumentation.