java怎么还是那么渣O(∩_∩)O哈哈~Class,Object这些混淆的概念

咳咳,今天又来更新下博客,看题目就知道本人现在的阶段怎么样,O(∩_∩)O哈哈~,不好意思,不能给大家带来叹为观止的文章,不过会一直更新自己在学习过程中学到的东西,谢谢。
今天要更新的内容是我昨晚一直在查的内容,就是java中class还有object还有reference这些到底是什么?
首先,在stackoverflow有看到一个解释很好:
http://stackoverflow.com/questions/9224517/what-is-a-class-reference-and-an-object



首先呢,class就像是蓝图,比方我们建一个房子,要先构造一下蓝图,房子的大概构造,通过这个蓝图就能构造出我们所谓的对象object;
而object呢就是工程师还有建筑队这些施工好了以后交付给我们的房子;
而reference就好比是房子的地址啦,通过这个地址我们可以找到我们的地址,这就是引用的作用。

那大家可能会问那对象跟引用又有什么区别?比如:

Television tel = new Television();

这个tel我们习惯上会以为是对象,其实准确些说是对象的名字,就像你本人,你有你的名字一样,这个tel就相当于你的名字,代表你本人,但这个名字除了代表你本人之外并没有其他额外的动作来操作你本人完成吃饭睡觉这些动作这些动作都只能由对象本身来完成,所以准确的说new Televison()这个才是对象了。所以说呢,这个tel应当是对象的引用,而不是对象,他同时充当两个角色,一个角色就是对象的名字,一个角色就是对象的引用。



大家都知道在java中是按值传递的,就算是我们把引用作为参数传给方法,传的也是值,这个值就是对象的地址。比如我们给方法传递参数,看下面代码:

public static void main(String[] args){
    Television tel = new Television();
    Television tel02 = new Television();
    tel02.pass(Object obj);
}
public class Televison{
    public void pass(Object obj){
    }
}

像上面的代码,传入pass()函数的参数其实传的不是大家以为的引用,传的是值,那个值就是tel存储的new Television()这个对象的地址。

有的人可能在看到这个代码出来的结果出来会一直搞不懂为什么结果是那样子:

这里写图片描述

得到的结果是输出来”ab”。

change()函数中的x只存在于方法change()中,这里得这么看:
我们把函数改成这样子更容易理解些:

public static void main(String[] args) {
    String x(原来) = new String("ab");
    change(x(原来));
    System.out.println(x(原来));
}

public static void change(String x(后来)) {
    x(后来) = "cd";
}

这里只是为了便于理解才这样写的。

其实我们把x(原来)传入x(后来)的时候,发生了下面的变化:
首先x(原来)会创建一个副本,我们暂时称之为x(原来的副本),其实就是复制一下啦,我们知道x(原来)存储的是对象的地址,而且java是按值传递的,所以x(原来的副本)存储的就是对象的地址。
然后呢,x(原来的副本)赋值给了x(后来):
x(后来) = x(原来的副本);
之后x(后来) = “cd”;
这里的x(后来)的内容就变了,原本储存的地址变成了”cd”。
但是我们知道,java中基本类型还有引用类型的存储方式是不同的,有什么不同呢?哈哈,前方高能了:
java中的基本类型有8种:int, short, long, byte, float, double, boolean, char(注意,并没有string的基本类型),这8种基本类型的存储是这样的,比如我们举个例子:

int a = 3;
int b = 3;

这里的a是一个指向int类型的引用,指向3这个字面值。这些字面值的数据,由于大小可知,生存期可知(这些字面值固定定义在某个程序块里面,程序块退出后,字段值就消失了),出于追求速度的原因,就存在于栈中。

另外,栈有一个很重要的特殊性,就是存在栈中的数据可以共享。

编译器先处理int a = 3;首先它会在栈中创建一个变量为a的引用,然后查找有没有字面值为3的地址,没找到,就开辟一个存放3这个字面值的地址,然后将a指向3的地址。接着处理int b = 3;在创建完b的引用变量后,由于在栈中已经有3这个字面值,便将b直接指向3的地址。

这样,就出现了a与b同时均指向3的情况。特别注意的是,这种字面值的引用与类对象的引用不同。
假定两个类对象的引用同时指向一个对象,如果一个对象引用变量修改了这个对象的内部状态,那么另一个对象引用变量也即刻反映出这个变化。
相反,通过字面值的引用来修改其值,不会导致另一个指向此字面值的引用的值也跟着改变的情况。 如上例,我们定义完a与b的值后,再令a=4;那么,b不会等于4,还是等于3。在编译器内部,遇到a=4;时,它就会重新搜索栈中是否有4的字面值,如果没有,重新开辟地址存放4的值;如果已经有了,则直接将a指向这个地址。因此a值的改变不会影响到b的值。

另一种是包装类数据,如Integer, String, Double等将相应的基本数据类型包装起来的类。这些类数据全部存在于堆中,Java用new()语句来显示地告诉编译器,在运行时才根据需要动态创建,因此比较灵活,但缺点是要占用更多的时间。

String是一个特殊的包装类数据。

关于String str = “abc”的内部工作。Java内部将此语句转化为以下几个步骤:

(1)先定义一个名为str的对String类的对象引用变量:String str;

(2)在栈中查找有没有存放值为”abc”的地址,如果没有,则开辟一个存放字面值为”abc”的地址,接着创建一个新的String类的对象o,并将o的字符串值指向这个地址,而且在栈中这个地址旁边记下这个引用的对象o。如果已经有了值为”abc”的地址,则查找对象o,并返回o的地址。

(3)将str指向对象o的地址。
值得注意的是,一般String类中字符串值都是直接存值的。但像String str = “abc”;这种场合下,其字符串值却是保存了一个指向存在栈中数据的引用!
为了更好地说明这个问题,我们可以通过以下的几个代码进行验证。
String str1 = “abc”;
String str2 = “abc”;
System.out.println(str1==str2); //true

注意,我们这里并不用str1.equals(str2);的方式,因为这将比较两个字符串的值是否相等。==号,根据JDK的说明,只有在两个引用都指向了同一个对象时才返回真值。而我们在这里要看的是,str1与str2是否都指向了同一个对象。

我们再来更进一步,将以上代码改成:
String str1 = “abc”;
String str2 = “abc”;
str1 = “bcd”;
System.out.println(str1 + “,” + str2); //bcd, abc
System.out.println(str1==str2); //false

这就是说,赋值的变化导致了类对象引用的变化,str1指向了另外一个新对象!而str2仍旧指向原来的对象。上例中,当我们将str1的值改为”bcd”时,JVM发现在栈中没有存放该值的地址,便开辟了这个地址,并创建了一个新的对象,其字符串的值指向这个地址。

再修改原来代码:
String str1 = “abc”;
String str2 = “abc”;
str1 = “bcd”;
String str3 = str1;
System.out.println(str3); //bcd
String str4 = “bcd”;
System.out.println(str1 == str4); //true

str3这个对象的引用直接指向str1所指向的对象(注意,str3并没有创建新对象)。当str1改完其值后,再创建一个String的引用str4,并指向因str1修改值而创建的新的对象。可以发现,这回str4也没有创建新的对象,从而再次实现栈中数据的共享。
我们再接着看以下的代码。
String str1 = new String(“abc”);
String str2 = “abc”;
System.out.println(str1==str2); //false
创建了两个引用。创建了两个对象。两个引用分别指向不同的两个对象。
String str1 = “abc”;
String str2 = new String(“abc”);
System.out.println(str1==str2); //false

创建了两个引用。创建了两个对象。两个引用分别指向不同的两个对象。
以上两段代码说明,只要是用new()来新建对象的,都会在堆中创建,而且其字符串是单独存值的,即使与栈中的数据相同,也不会与栈中的数据共享。

数据类型包装类的值不可修改。不仅仅是String类的值不可修改,所有的数据类型包装类都不能更改其内部的值。

结论与建议:

(1)我们在使用诸如String str = “abc”;的格式定义类时,总是想当然地认为,我们创建了String类的对象str。担心陷阱!对象可能并没有被创建!唯一可以肯定的是,指向String类的引用被创建了。至于这个引用到底是否指向了一个新的对象,必须根据上下文来考虑,除非你通过new()方法来显要地创建一个新的对象。因此,更为准确的说法是,我们创建了一个指向String类的对象的引用变量str,这个对象引用变量指向了某个值为”abc”的String类。清醒地认识到这一点对排除程序中难以发现的bug是很有帮助的。

(2)使用String str = “abc”;的方式,可以在一定程度上提高程序的运行速度,因为JVM会自动根据栈中数据的实际情况来决定是否有必要创建新对象。而对于String str = new String(“abc”);的代码,则一概在堆中创建新对象,而不管其字符串值是否相等,是否有必要创建新对象,从而加重了程序的负担。

(3)当比较包装类里面的数值是否相等时,用equals()方法;当测试两个包装类的引用是否指向同一个对象时,用==。

(4)由于String类的final性质,当String变量需要经常变换其值时,应该考虑使用StringBuffer类,以提高程序效率。

感谢百度知道中大神的回答:http://zhidao.baidu.com/link?url=QTzOQKVWgJA9HhXlwFtXGfAJpSGbkVW9UI45HcId1CeTu4ViUfOpqNMYG3lcmylfavLo2Obh62WbfEPdmfwytq

理解了上面的内容之后我们再回过头来讲:

public static void main(String[] args) {
    String x(原来) = new String("ab");
    change(x(原来));
    System.out.println(x(原来));
}

public static void change(String x(后来)) {
    x(后来) = "cd";
}

我们知道:

x(原来的副本)赋值给了x(后来):
x(后来) = x(原来的副本);
之后x(后来) = “cd”;
这里的x(后来)的内容就变了,原本储存的地址变成了”cd”。
从上面的解释我们知道,x(原来的副本)和x(后来)刚开始共用一个地址,但x(后来)的内容变了之后,会先在栈中查找有没有”cd”,如果没有的话,就会在栈中重新创建一个存放”cd”的对象,我们假设是o吧,o存放的是”cd”在栈中的地址,然后x(后来)相当于o对象的引用,存放o的地址,通过o的地址可以找到o对象,找到o对象从栈中的存放的地址可以找到”cd”,就是这个过程,所以x(后来)其实只是重新寻找了一个”归处”,而x(原来的副本)其实还是坚守原来的位置的。

好了,今天的博文就更新到这里,谢谢大家!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值