关键字final“不可变的对象”与“不可变的对象引用”

final

对于一个final变量,如果是基本数据类型的变量,则其数值一旦在初始化之后便不能更改;
如果是引用类型的变量,则在对其初始化之后便不能再让其指向另一个对象,但是原先对象里的数据是可以修改的。

例如:

final List<Integer> list = new ArrayList<Integer>();
list.add(1);
list.add(2);

这里不会报错。

但是,一旦修改list这个引用本身,就会报错:

final List<Integer> list = new ArrayList<Integer>();
list.add(1);
list.add(2);
list = new ArrayList<Integer>();  //error: cannot assign a value to final variable list

String

string类型的常量不可变和final看起来有一些不一样,比如我们经常看到这样的代码:

String str = new String("abc");
str = "cde";

String常量的不可变指的是存放String对象那个区域里的内容不能变。比如,上面的代码。堆里面会有存放abc的区域,而这个区域的abc是不能变的,也就是不能变为abd等。而引用是可变的,只要不改变常量里面的内容就行。

StringBuilder

Java中的String类是声明为final的,一旦建立String对象之后它的值的内容是无法修改的,任何看上去是对String对象的值的修改都只是改变String对象的引用,但是StringBuffer类弥补了String类的不足,StringBuffer类的内容可以修改。
StringBuilder 在api的定义是:

public final class StringBuilder
     extends AbstractStringBuilder
     implements java.io.Serializable, CharSequence 

也是final,
区别是:
String是不可变类,一旦String对象被创建,包含在对象中的字符序列是不可变的,直到对象被销毁
StringBuffer 与 StringBuilder对象则是可变的,不用每次新建对象,效率高!

final StringBuilder sb = new StringBuilder("haha");  
StringBuilder sbChange = new StringBuilder("hehe");  

//同一对象的hashCode值相同  
System.out.println("sb中的内容是:"+sb);  
System.out.println(sb+"的哈希编码是:"+sb.hashCode());  
sb.append("值改变了");  
System.out.println("sb中的内容是:"+sb);  
System.out.println(sb+"的哈希编码是:"+sb.hashCode()); 

// sb = sbChange; 这句代码会报错,因为sb是final的,如果把sb的final去掉那么就不报错

输出:

sb中的内容是:haha
haha的哈希编码是:2018699554
sb中的内容是:haha值改变了
haha值改变了的哈希编码是:2018699554

final 与 static

final 和 static 在一起使用就会发生神奇的化学反应,他们同时使用时即可修饰成员变量,也可修饰成员方法。
对于成员变量,该变量一旦赋值就不能改变,我们称它为“全局常量”。可以通过类名直接访问。
对于成员方法,则是不可继承和改变。可以通过类名直接访问。

System.arraycopy()

首先明确一点,System.arraycopy 操作的是数组,效果是深复制。 是不是觉得怎么和你印象的中不一样?
重点来了,对于对象数组,例如: User[],这种数组,有一个注意点,这个点就是:对于数组内的对象是浅拷贝。

一句话:
System.arraycopy 对于数组是深拷贝,对于数组内的对象是浅拷贝。因为操作的传入参数是数组,那么回归本意,效果是深复制。

证明:

public class Test {
    public static void main(String[] args) {

        User[] users=new User[]{
                new User("111"),new User("222"),new User("333")
        };//初始化对象数组
        User[] target=new User[users.length];//新建一个目标对象数组

        System.arraycopy(users, 0, target, 0, users.length); // 复制

        System.out.println("数组地址是否一样:"+(users == target?"浅复制":"深复制"));
        System.out.println("数组内对象地址是否一样:"+(users[0] == target[0]?"浅复制":"深复制"));

        target[0].setEmail("444");
        System.out.println("修改后输出 users[0] ,是否和 target[0]一样是444,users[0]:"+users[0].getEmail());

        users[0] = null; // -----------------①
        System.out.println("遍历 users");
        for (User user : users){
            System.out.println(user);
        }
        System.out.println("遍历 target");
        for (User user : target){
            System.out.println(user);
        }
        
        users = null;
        if(target == null){
            System.out.println("users = null 后是否 target 也变成了 null,是则证明是浅复制");
        }else{
            System.out.println("users = null 后是否 target 不受任何影响,是则再次证明数组是深复制");
        }
    }
}
class User{
    private String email;
    public User(String email) { this.email = email; }
    public String getEmail() { return email;}
    public void setEmail(String email) { this.email = email; }
    @Override
    public String toString() { return "User [email=" + email+ "]"; }
}

输出的结果如下:

数组地址是否一样:深复制
数组内对象地址是否一样:浅复制
修改后输出 users[0],是否和 target[0]一样是444,users[0]:444
遍历 users
null
User [email=222]
User [email=333]
遍历 target
User [email=444]
User [email=222]
User [email=333]
users = null 后是否 target 不受任何影响,是则再次证明数组是深复制
String[] s1 = new String[]{"Hi","Hi","Hi"};
String[] s2 = new String[s1.length];
System.arraycopy(s1, 0, s2, 0, s1.length);
s1[2]="Hier";
System.out.println("s1==s2 ? " + (s1==s2));
System.out.println("s1[0]==s2[0] ? " + (s1[0]==s2[0]));
System.out.println(Arrays.toString(s1));
System.out.println(Arrays.toString(s2));

int[] i1 = new int[]{1,2,3};
int[] i2 = new int[i1.length];
System.arraycopy(i1, 0, i2, 0, i1.length);
i1[2]=5;
System.out.println(Arrays.toString(i1));
System.out.println(Arrays.toString(i2)); 

输出:

s1==s2 ? false
s1[0]==s2[0] ? true
[Hi, Hi, Hier]
[Hi, Hi, Hi]
[1, 2, 5]
[1, 2, 3]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值