java final 关键字

说到final关键字,搞过java的都不陌生。基本上每个码农对final的基本用法都清楚,但是经常看到一些奇怪的用法和理解,那今天就好好梳理下。

1.final关键字的基本用法
在Java中,final关键字可以用来修饰类、方法和变量(包括成员变量和局部变量)

1.1修饰类
当final修饰一个类时,表明这个类不能被继承。也就是说,如果一个类你永远不会让他被继承,就可以用final进行修饰。默认final类中的方法都是final,成员变量可以设为final也可以不是。

[java]
package com.learn.core.ch02;

public final class Country {

private String name;

public void display() {

}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public Country(String name) {
this.name = name;
}
}

public class CountrySub extends Country{
//The type CountrySub cannot subclass the final class Country
}

[/java]

1.2 修饰方法
下面这段话摘自《Java编程思想》第四版第143页:

“使用final方法的原因有两个。第一个原因是把方法锁定,以防任何继承类修改它的含义;第二个原因是效率。在早期的Java实现版本中,会将final方法转为内嵌调用。但是如果方法过于庞大,可能看不到内嵌调用带来的任何性能提升。在最近的Java版本中,不需要使用final方法进行这些优化了。“

因此,如果只有在想明确禁止 该方法在子类中被覆盖的情况下才将方法设置为final的。
注:类的private方法会隐式地被指定为final方法。

1.3 修饰变量(成员变量或者局部变量)
对于一个final变量,如果是基本数据类型的变量,则其数值一旦在初始化之后便不能更改;如果是引用类型的变量,则在对其初始化之后便不能再让其指向另一个对象。注意如果是引用类型的变量,只是说引用不能再变,但是引用所代表的对象还是可以改变该对象内在的行为。
成员变量:必须初始化或者在构造器中初始化

[java]
package com.learn.core.ch02;

public final class Country {

private String name;

public Country(String name) {
this.name = name;
}

public static void main(String[] args) {
final Country country = new Country("china");
country.display();

// update to us
country.setName("US");
country.display();

// output
// china
// US

// country = new Country("GE");
// error The final local variable country cannot be assigned. It must be blank and not using a compound
// assignment
}

public void display() {
System.out.println(name);
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

}

[/java]

2. 深究final关键字
2.1 类的final变量和普通变量有什么区别?
当用final作用于类的成员变量时,成员变量(注意是类的成员变量,局部变量只需要保证在使用之前被初始化赋值即可)必须在定义时或者构造器中进行初始化赋值,而且final变量一旦被初始化赋值之后,就不能再被赋值了
2.2 final的引用变量指向的对象内容可变吗?
只要不改变引用,引用所指向的对象是可以改变内在行为或者外在行为的,就是可以调各种方法。可以参考上边的例子。
2.3 匿名内部类中使用的外部局部变量为什么只能是final变量?
关于这个,可以参考另外一篇文章。
2.4 static 和final一起使用,可以定义不可变的常量
2.5 所有的局部变量定义成final,除了匿名内部类可以使用的有点,还有其他优势吗?

[java]
public void display() {
System.out.println(name);
String title = "Hello";
Date date = new Date();
System.out.println(title + "," + date);
}
[/java]
001.png
局部变量

[java]
public void display() {
System.out.println(name);
final String title = "Hello";
final Date date = new Date();
System.out.println(title + "," + date);
}
[/java]

final局部变量

通过查看字节码,发现final修饰的局部变量在编译阶段就会被替换掉,有点类似C语言的宏定义,如果没有final修饰的局部变量,会在方法运行栈中多用2个指令(aload_)去存储和加载局部变量所指向的值

12: astore_1
13: getstatic #31 // Field java/lang/System.out:Ljava/io/PrintStream;
16: aload_1

所以final局部变量确实可以减少指令,提升程序的性能,但是我认为如果所有的局部变量都用final修饰,会影响代码的整洁。至于选择性能还是代码的可阅读性,要看自己的习惯了!

欢迎大家拍砖纠正!

参考资料
深入理解Java中的final关键字
浅析Java中的final关键字

Linux 命令(3) mount

在unix系统中,所有可以访问的文件都是一个树形的结构。unix文件系统,根目录为/。某些文件可以来源于不同的设备中。 mount命令主要是用来挂载各种各样的设备到这个树形结构上。unmount命令用来卸载对应的设备。

命令格式为:
mount [-fnrsvw] [-t vfstype] [-o options] device dir

常用的命令如下:
1.mount -t type device dir
挂载给定的设备到目录dir。假如之前的dir有内容,挂载之后便不可见,只有卸载之后才能重新可见,所以一般挂载的目录最好是新目录
2.mount -h | mount -v
查看一些帮助信息,版本
3.mount [-l] [-t type]
列出当前挂载的所有的设备
[bash]
[caveup0@iZ23myrure3Z ~]$ mount -l
/dev/xvda1 on / type ext4 (rw)
proc on /proc type proc (rw)
sysfs on /sys type sysfs (rw)
devpts on /dev/pts type devpts (rw,gid=5,mode=620)
tmpfs on /dev/shm type tmpfs (rw)
none on /proc/xen type xenfs (rw)
none on /proc/sys/fs/binfmt_misc type binfmt_misc (rw)
[/bash]
4.mount –bind olddir newdir
可以将文件系统同时绑定到两个不同的位置。其短格式:mount -B olddir newdir 或者在 fstab 中添加如下的条目:
/olddir /newdir none bind
这样,就可以从两个不同的位置访问完全相同的内容了。
甚至可以将同一个文件或目录绑定到自身(只是创建了一个挂载点而已):
mount –bind foo foo

上面的绑定挂载只能绑定一个单独的文件系统,而不包含其下级子目录上的文件系统。
如果想要递归绑定整个目录树上所有的文件系统,可以使用:
mount –rbind olddir newdir
或者其短格式:
mount -R olddir newdir

使用 –bind/–rbind 绑定挂载文件系统的时候,并不能改变其原有的挂载选项。
如果想要改变挂载选项,必须在绑定之后,再使用 remount 选项来修改:

更详细的命令,大家可以调 man mount。 欢迎大家拍砖!

LINUX 命令(2) cut

今天我要介绍cut命令,一个非常简单也好用的命令,可以迅速地从每行找到你想要的文本

语法:

[bash]
cut [-bn] [file] 或 cut [-c] [file] 或 cut [-df] [file]
[/bash]

1.按字节来选择文本,命令: cut -b

[bash]
[caveup0@iZ23myrure3Z ~]$ who |cut -b 3
v
[caveup0@iZ23myrure3Z ~]$ who |cut -b 3-5
veu
[caveup0@iZ23myrure3Z ~]$ who |cut -b 1-5
caveu
[caveup0@iZ23myrure3Z ~]$
[/bash]

2.按字符来选择文本,命令: cut -c

[bash]
[caveup0@iZ23myrure3Z ~]$ who |cut -c 2-3
av
[caveup0@iZ23myrure3Z ~]$
[/bash]

看了上边两个列子,好像-b和-c差不多,其实两个是不一样的。一个是用字节来分割,一个是用字符来分割。
一般ansi编码字符和字节是一样的,但是UTF8或者其他的编码,字节和字符是不相等的,大家可以试试中文


[bash]
[caveup0@iZ23myrure3Z ~]$ cat a.txt
我是一个人
[caveup0@iZ23myrure3Z ~]$ cut -b 2 a.txt

[caveup0@iZ23myrure3Z ~]$ cut -c 2 a.txt

[caveup0@iZ23myrure3Z ~]$
[/bash]

3.按给定的分隔符来选择文本,命令: cut -d <分隔符> -f
用-d来设置间隔符,然后用-f来设置我要取的是字段。
注意:cut只擅长处理“以一个字符间隔”的文本内容,如果想以多个字符来分割,可以结合sed来搭配使用。

[bash]
[caveup0@iZ23myrure3Z ~]$ cat /etc/passwd |head -n 4 |cut -d ':' -f1
root
bin
daemon
adm
[caveup0@iZ23myrure3Z ~]$
[/bash]

欢迎大家有更好的见解和使用心得!

Linux 命令(1) rm

一直在用linux,对linux运维和配置也比较熟悉,所以计划结合自己的经验,写一些linux的文章。欢迎大家留言和拍砖!!

熟悉Linux的朋友们,一定经常用rm这个command。而经常有一些朋友喜欢直接加上参数-rf。所以当误删除的时候,一定非常怀念window系统,因为它有个回收站。这样可以很方便的找出来。但是在linux下如何避免这样的问题发生呢。

自己简单地总结了2个方法:

1. alias rm .不能直接call rm command,需要写完整的路径来调用rm command。比如:

alias rm=’ echo call /bin/rm -i’

这样可以简单的提醒下。

2. 改变默认的rm 行为。

可以通过自己写一个bash script。让rm调用这个bash script。而这个bash script可以实现如何提醒或者不删除,而把要删除的文件移动到另外的地方或者目录。

自己简单实现了一个可以move和double check 的rm。具体代码如下。希望拍砖啊。 #!/bin/bash

[bash]
promt_usage(){
cuser=`who am i |awk '{print $1}'`
echo "****************************************"
echo "* *"
echo "* \\\\---$cuser---//// *"
echo "* Delete: Please Be Careful *"
echo "* *"
echo "****************************************"
}
promt_mv(){
echo "**************************************"
echo "* *"
echo "* Current status:pre-moving *"
echo "* move files:$@ *"
echo "* *"
echo "**************************************"
}
drm(){
promt_usage
BACK_UP=no
PROMT=no
RECURSIVE=no
FORCE=no
FILE=""
while getopts birf opt
do
case $opt in
b) BACK_UP=yes
;;
r) RECURSIVE=yes
;;
f) FORCE=yes
;;
i) PROMT=yes
;;
'?')
;;
esac
done
#the destination file
while [ "$#" != 0 ]
do
shift
if [[ $@ != -* ]]
then
break
fi
done
FILE="$@"
#double check rm
if [ "x$BACK_UP" = "xyes" ]
then
promt_mv $FILE
mv $FILE /tmp
else
echo "Are you sure to delete these files(y/n)?"
read ack
echo "Are you sure to delete these fiiles(y/n),really?"
read ack2
#double check
if [[ "x$ack" == "x$ack2" ]] && [[ "x$ack" == "xy" ]]
then
/bin/rm "$@"
else
echo "Please check your input!"
fi
fi

}
drm "$@"
[/bash]


[bash]
alias rm='bash /home/xliang/.drm.sh -i'
[/bash]

这样可以简单地避免一些,但是还是需要自己的细心才能解决根本问题。每次调用rm的时候,一定确定自己对rm做的操作很有信心,否则后悔都来不及!

Ant 项目构建(2)

上一篇介绍了一下Ant的产生和特点,在最后给大家看看了build.xml。其实Ant的核心就是build.xm。如何编写适合自己工程的build.xml是最重要的。

  • build.properties

tomcat.home=F:\\Java\\Tomcat 5.5
webapps.home=F:\\Java\\Tomcat 5.5\\webapps

build.properties文件是存储一些公共变量的,比如你机子上tomcat的绝对路径。
其实也用到了面向对象的原理,如果你的环境变了,只需要更改build.properties文件就可以了。而不用去更改build.xm里的相关属性。build.properties也可以加上你想加的东西,比如公共的jar文件。。

  • build.xml

project代表一个工程,有4个常见属性:
(1). default 表示默认的运行目标,是必须的
(2). basedir 表示工程的基准目录,”.”表示和build.xml所在的目录。
(3). name 表示工程的名字
(4).description 对工程的描述,可写可不写,写上更清楚对以后的debug

  • build.xml的target

每个build.xml只有个project,但是可以有多个target(目标).比如:

[xml]

<target name="compile" depends ="prepare">
<javac srcdir="${src.home}" destdir="${classes.home}" debug="yes">
<classpath refid = "compile.classpath"/>
</javac>
</target>

[/xml]

介绍完target,大家就会对build.xml有了一个更清楚的认识,更深地清楚了build.xml的框架.更详细的,可以去访问apache ant主页。

apache ant

ant手册

欢迎大家拍砖!