您的位置:首页 > 编程语言 > Java开发

Java笔记:final修饰符

2013-06-13 22:44 162 查看
本篇笔记主要是final修饰符修饰类、成员变量、方法的用法,不可变类,实例缓存的不可变类

final关键字可以用来修饰类、变量、方法。final变量不能重新赋值,子类不能覆盖父类的final方法,final类不能有子类。

1.final修饰的成员变量
(1)final修饰的成员变量一旦赋值后,不能被重新赋值。
(2)final修饰的实例Field,要么在定义该Field的时候指定初始值,要么在普通初始化块或构造器中指定初始值。但是如果在普通初始化块中为某个实例Field指定了初始值,则不能再在构造器中指定初始值。
(3)final修饰的类Field,要么在定义该Field的时候指定初始值,要么在静态代码块中定义初始值。
(4)如果在构造器或初始化块中对final成员变量进行初始化,则不要在初始化之前就访问该成员的值。

package cn.lsl;

public class FinalTest {
final int a = 5;    //直接赋值

final String str;        //普通代码块中赋值
{
str = "zhangsan";
}

final int b;            //构造器中赋值
public FinalTest(){
b = 7;
}

final static int c = 8;        //直接赋值

final static int d;            //静态代码块中赋值
static{
d = 9;
}

//如果在构造器或初始化块中对final成员变量进行初始化,则不要在初始化之前就访问该成员的值。
final int age;
{
//System.out.println(age);
age = 22;
System.out.println(22);
}
}


2.final修饰的局部变量
(1)系统不会对局部变量进行初始化,布局变量必须要显示的初始化。所以使用final修饰的局部变量,既可以在定义的时候指定默认值,也可以不指定默认值。
(2)final修饰形参的时候,不能为该形参赋值。

3.final修饰基本数据类型变量和修饰引用类型变量的区别
使用final修饰基本类型的变量,一旦对该变量赋值之后,就不能重新赋值了。但是对于引用类型变量,他保存的只是引用,final只能保证引用类型变量所引用的地址不改变,但不保证这个对象不改变,这个对象完全可以发生改变。

eg:

final Person p = new Person();
p.setAge(23);        //改变了Person对象的age Field
//p=null            //编译出错


final修饰的引用类型变量不能被重新赋值,但是可以改变引用变量所引用对象的内容。

4.final的“宏变量”
(1)final修饰符的一个重要用途就是“宏变量”。当定义final变量时就该为该变量指定了初始值,而且该初始值可以在编译时就确定下来,那么这个final变量本质上就是一个“宏变量”,编译器会把程序中
所有用到该变量的地方直接替换成该变量的值。

package cn.lsl;

public class FinalTest {
public static void main(String[] args){
final String name = "小明" + 22.0;
final String name1 = "小明" + String.valueOf(22.0);
System.out.println(name == "小明22.0");
System.out.println(name1 == "小明22.0");
}
}


final String name1 = "小明" + String.valueOf(22.0);中调用了String类的方法,因此编译器无法再编译的时候确定name1的值,所以name1不会被当成“宏变量”。

package cn.lsl;

public class FinalTest {
public static void main(String[] args){
String s1 = "小明";
String s2 = "小" + "明";
System.out.println(s1 == s2);    //true

String str1 = "小";
String str2 = "明";
String s3 = str1 + str2;
System.out.println(s1 == s3);        //false

//宏替换
final String str3 = "小";
final String str4 = "明";
String s4 = str3 + str4;
System.out.println(s1 == s4);        //true
}
}


分析:1.java会使用常量池量管理曾经使用过的字符串直接量。String a = "hello"; 那么字符串池中会缓存一个字符串"hello",当执行String b = "hello";会让b直接指向字符串池中的"hello"字符串。所以a==b返回true。
2.String s3 = str1 + str2;编译时无法确定s3的值
3.String s4 = str3 + str4;因为执行了宏替换,所以在编译的时候就已经确定了s4的值

5.用final修饰的方法不能被重写。用final修饰的类不能有子类。

6.不可变类
不可变类是指创建该类的实例后,该实例的Field是不可改变的。
如果创建自定义的不可变类,应该遵循如下规则
(1)使用private和final修饰符来修饰该类的Field。
(2)提供带参数的构造器,用于传入参数来初始化类里的Field。
(3)仅为该类的Field提供getter方法,不要为该类的Field提供setter方法。
(4)如果有必要,重写Object类的hashCode和equals方法。

package cn.lsl;

public class Address {
private final String detail;
private final String postCode;

public Address() {
this.detail = "";
this.postCode = "";
}
public Address(String detail, String postCode) {
this.detail = detail;
this.postCode = postCode;
}
public String getDetail() {
return detail;
}
public String getPostCode() {
return postCode;
}

public boolean equals(Object obj){
if(this == obj){
return true;
}
if(obj !=null && obj.getClass() == Address.class){
Address ad = (Address)obj;
if(this.getDetail().equals(ad.getDetail()) && this.getPostCode().equals(ad.getPostCode())){
return true;
}
}
return false;
}

public int hashCode(){
return detail.hashCode() + postCode.hashCode() * 31;
}

}


因为final修饰引用类型变量时,表示这个引用变量不可重新被赋值,但引用类型变量所指向的对象依然可被改变。所以在创建不可变类的时候,如果包含的Field类型是可变的,那么这个不可变类就创建失败了。

如下:

package cn.lsl;

class Name{
private String firstName;
private String lastName;

public Name() {
super();
// TODO Auto-generated constructor stub
}
public Name(String firstName, String lastName) {
super();
this.firstName = firstName;
this.lastName = lastName;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
}

public class Person {
private final Name name;
public Person(Name name){
this.name = name;
}
public Name getName(){
return name;
}
public static void main(String[] args) {
Name n = new Name("明","小");
Person p = new Person(n);
System.out.println(p.getName().getFirstName());
n.setFirstName("君");
System.out.println(p.getName().getFirstName());
}
}


通过n.setFirstName("君");改变了firstName。
为了保证对象的不可变性
可以修改为如下代码

package cn.lsl;

class Name{
private String firstName;
private String lastName;

public Name() {
super();
// TODO Auto-generated constructor stub
}
public Name(String firstName, String lastName) {
super();
this.firstName = firstName;
this.lastName = lastName;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
}

public class Person {
private final Name name;
public Person(Name name){
//this.name = name;
this.name = new Name(name.getFirstName(), name.getLastName());
}
public Name getName(){
//return name;
return new Name(name.getFirstName(), name.getLastName());
}
public static void main(String[] args) {
Name n = new Name("明","小");
Person p = new Person(n);
System.out.println(p.getName().getFirstName());
n.setFirstName("君");
System.out.println(p.getName().getFirstName());
}
}


7.实例缓存的不可变类
如果程序需要经常使用想用的不可变类实例,则应该考虑缓存这种不可变类的实例,因为重复创建相同的对象没有太大的意义,而且加大系统的开销。
可以使用一个数组来作为缓存池,实现不可变类。

package cn.lsl;

class CacheImmutale {
private static int MAX_SIZE = 10;
//使用数组来缓存实例
private static CacheImmutale[] cache = new CacheImmutale[MAX_SIZE];
private static int pos = 0;
private final String name;
private CacheImmutale(String name){
this.name = name;
}
public String getName(){
return name;
}

public static CacheImmutale valueOf(String name){
for(int i=0; i < MAX_SIZE; i++){
if(cache[i] != null && cache[i].getName().equals(name)){
return cache[i];
}
}
if(pos == MAX_SIZE){
cache[0] = new CacheImmutale(name);
pos = 1;
}else{
cache[pos++] = new CacheImmutale(name);
}
return cache[pos-1];
}

public boolean equals(Object obj){
if(this == obj){
return true;
}
if(obj != null && obj.getClass() == CacheImmutale.class){
CacheImmutale ci = (CacheImmutale)obj;
return name.equals(ci.getName());
}
return false;
}
public int hashCode(){
return name.hashCode();
}
}

public class CacheImmutaleTest{
public static void main(String[] args) {
CacheImmutale c1 = CacheImmutale.valueOf("hello");
CacheImmutale c2 = CacheImmutale.valueOf("hello");
System.out.println(c1 == c2);
}
}


分析:以上程序缓存池采用“先进先出”规则来决定哪个对象被移除缓存池。
程序中还使用了private修饰来隐藏该类的构造器,通过提供该类的valueOf方法来获取实例。

Java提供的java.lang.Integer类也是采用类似的策略来处理的,但是只能Integer缓存-128~127之间的Integer对象。

package cn.lsl;

public class IntegerTest {
public static void main(String[] args) {
Integer a = new Integer(23);
Integer b = Integer.valueOf(23);
Integer c = Integer.valueOf(23);
System.out.println(a == b);
System.out.println(b == c);

//Integer只能缓存-128~127之间的Integer对象
Integer d = Integer.valueOf(230);
Integer e = Integer.valueOf(230);
System.out.println(d == e);
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: