第十六天

Posted jikebin

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了第十六天相关的知识,希望对你有一定的参考价值。

Object类是一个特殊的类,是所有类的父类,如果定义一个类没有用extends明确指出继承于某个类,那么它默认继承Object类。

Object是类层次结构的根类

所有对象,包括数组在内,都实现了这个类中的方法

 

Object类没有属性,只有方法,而且我们可以从源码中看到大多数方法都是native方法:

native关键字做一个总结:

·        native关键字是JavaC++/C联合开发的时候用的,java自己开发不用!

·        使用native关键字说明这个方法是原生函数,也就是这个方法是用C/C++语言实现的,并且被编译成了dll,由java去调用。这个函数的实现是在DLL中,JDK的源代码中并不包含,所以看不到被这个关键字修饰的函数的源代码。对于不同的平台它们是不同的。这也是java的底层机制,实际上java就是在不同平台上调用不同的native方法实现对操作系统的访问。

·        简言之,native是用做java和其他语言(如C)进行协作时用的,也就是native后的函数的实现不是用java写的。

·        native的意思就是通知操作系统,这个函数你必须给我实现,因为我要用。所以native关键字的函数就是操作系统实现的,java只能调用。

·        java是跨平台的语言,既然是跨平台,所付出的代价就是牺牲一些对底层的控制,而java要实现对底层的控制,就需要一些其他语言的帮助,这个就是native的作用了

 

Object的主要常用方法做个了解:

1public final nativeClass<?> getClass()方法

该方法也是native方法,返回的是该Object对象的类对象/运行时类对象。

类对象: 在java中,类是对具有一组相同特征或者行为的实例的抽象并进行描述,对象是该类所描述的特征或者行为的具体实例。作为概念层次的类,其本身也具有某些共同的属性,如都具有类型、类加载器、包、父类、属性、方法等。java中有专门定义一个类,Class,该类用于描述其他类所具有的这些特征。因此,从该角度来看,类本身也都是属于Class类的对象。 该部分涉及到反射的知识。

 

2 public native int hashCode()方法

hashCode返回的值:一般是通过将该对象的内存地址转换成一个整数来实现。该方法不是java实现的,因此使用了native关键字。

 

3 public boolean equals(Object obj)方法

有关equals()方法与==运算符的区别,前面的课程中有过讲解,但是在这里,还要进一步的讨论一个问题:重写equals方法时一定要重写hashCode方法,为什么?

我们知道在Object类中,hashCode方法是通过Object对象的地址计算出来的,因为Object对象只与自身相等,所以同一个对象的地址总是相等的,计算取得的哈希码也必然相等,对于不同的对象,由于地址不同,所获取的哈希码自然也不会相等。因此到这里我们就明白了,如果一个类重写了equals方法,但没有重写hashCode方法,将会直接违反重写equals方法必须要有对称性原则:对于任何非null的引用值,当x.equals(y)返回true时,y.equals(x)一定返回true的规定,这样的话,,就无法达到我们预期想要的效果。

用例子来说明:

对于s1s2的结果,我们并不惊讶,因为相同的内容的s1s2获取相同内的value这个很正常,因为String类重写了equals方法和hashCode方法,使其比较的是内容和获取的是内容的哈希码。

 

但是对于k1k2的结果就不太尽人意了,k1获取到的值是2k2获取到的是null,这是为什么呢?想必大家已经发现了,Key只重写了equals方法并没有重写hashCode方法,这样的话,equals比较的确实是内容,hashCode方法呢?没重写,那就肯定调用超类ObjecthashCode方法,这样返回的不就是地址了吗?k1k2属于两个不同的对象,返回的地址肯定不一样,所以现在我们知道调用map2.get(k2)为什么返回null了吧!

 

解决也很简单,重写hashCode方法,返回equals方法判断相等的关键属性的hashCode值:

记住结论:给一个类重写equals方法时一定要重写hashCode方法

 

3protected native Object clone( )

clone()方法又是一个被声明为native的方法,因此,我们知道了clone()方法并不是Java的原生方法,具体的实现是有C/C++完成

clone英文翻译为克隆,其目的是创建并返回此对象的一个副本。

Java术语表述为:clone函数返回的是一个引用,指向的是新的clone出来的对象,此对象与原对象分别占用不同的堆空间。

 

例子说明:

例子很简单,在main()方法中,new一个Oject对象后,想直接调用此对象的clone方法克隆一个对象,但是你会发现用不了!

 

why?回到Object类中clone()方法的定义,可以看到其被声明为protectedprotected修饰的属性或方法表示:在同一个包内或者不同包的子类可以访问。显然,Object类与ObjectTest类在不同的包中,但是ObjectTest继承自Object,是Object类的子类,于是,现在却出现子类中通过Object引用不能访问protected方法,原因在于对不同包中的子类可以访问没有正确理解。

 

不同包中的子类可以访问是指当两个类不在同一个包中的时候,继承自父类的子类内部且主调(调用者)为子类的引用时才能访问父类用protected修饰的成员(属性/方法)。 在子类内部,主调为父类的引用时并不能访问此protected修饰的成员

是的,因为此时的主调已经是子类的引用了。

上述代码在运行过程中会抛出”java.lang.CloneNotSupportedException”,表明clone()方法并未正确执行完毕,问题的原因在与Java中的语法规定:

clone()的正确调用是需要实现Cloneable接口,如果没有实现Cloneable接口,并且子类直接调用Object类的clone()方法,则会抛出CloneNotSupportedException异常。

Cloneable接口仅是一个标记接口,接口本身不包含任何方法,用来指示Object.clone()可以合法的被子类引用所调用。

于是,上述代码改成如下形式,即可正确指定clone()方法以实现克隆。

总结:

1. Obejct类的clone()方法实现的是浅拷贝

2. 在子类内部,主调为父类的引用时并不能访问此protected修饰的成员。调用者为子类的引用时才能访问父类中用protected修饰的成员

3. 想要在子类中调用父类的clone()方法,子类需要实现Cloneable接口,该接口用来指示Object.clone()可以合法的被子类引用的标记

4.简单谈谈什么时候需要用到clone方法呢,开发飞机大战的游戏,发射出来的子弹,每个子弹就是一个对象,这个时候就可以用clone方法复制子弹对象,它是一种浅拷贝,可以大量节约内存的开销。

5.clone()new的区别:

1)在javaclone()new都能创建对象。

2clone()不会调用构造方法;new会调用构造方法。

3clone()能快速创建一个已有对象的副本,即创建对象并且将已有对象中所有属性值克隆;new只能在JVM中申请一个空的内存区域,对象的属性值要通过构造方法赋值。

注意:

1)使用clone()类必须实现java.lang.Cloneable接口并重写Object类的clone()方法,如果没有实现Cloneable()接口将会抛出CloneNotSupportedException异常。(此类实现java.lang.Cloneable接口,指示Object.clone()方法可以合法的对该类实例进行按字段复制。)

2)默认的Object.clone()方法是浅拷贝,创建好对象的副本然后通过赋值拷贝内容,如果类包含引用类型变量,那么原始对象和克隆对象的引用将指向相同的引用内容。

面试题:什么是浅拷贝?什么是深拷贝?

浅拷贝:默认的Object.clone()方法,对于引用类型成员变量拷贝只是拷贝即地址,没有在堆中开辟新的内存空间。

深拷贝:重写clone()方法,拷贝一个对象时,不仅仅把对象的引用进行复制,还把该对象引用的值也一起拷贝,会在堆中开辟新的内存空间。

 

4public String toString()方法

当使用System.out.println(Object obj);时,返回的就是该obj对象的toString方法,实际上System.out.println()内部是通过toString实现的

getClass()返回对象的类对象,getClassName()String形式返回类对象的名称(含包名)。Integer.toHexString(hashCode())则是以对象的哈希码为实参,以16进制无符号整数形式返回此哈希码的字符串表示形式。

u1对象的哈希码是638,则对应的16进制为27e,调用toString()方法返回的结果为:com.corn.User@27e。包名.类名@哈希码

因此:toString()是由对象的类型和其哈希码唯一确定,同一类型但不相等的两个对象分别调用toString()方法返回的结果不可能相同,除非重写了toString()方法。

 

 

 

 

 

 

 

 

 

 

Object类是一个特殊的类,是所有类的父类,如果定义一个类没有用extends明确指出继承于某个类,那么它默认继承Object类。

Object是类层次结构的根类

所有对象,包括数组在内,都实现了这个类中的方法

 

Object类没有属性,只有方法,而且我们可以从源码中看到大多数方法都是native方法:

native关键字做一个总结:

·        native关键字是JavaC++/C联合开发的时候用的,java自己开发不用!

·        使用native关键字说明这个方法是原生函数,也就是这个方法是用C/C++语言实现的,并且被编译成了dll,由java去调用。这个函数的实现是在DLL中,JDK的源代码中并不包含,所以看不到被这个关键字修饰的函数的源代码。对于不同的平台它们是不同的。这也是java的底层机制,实际上java就是在不同平台上调用不同的native方法实现对操作系统的访问。

·        简言之,native是用做java和其他语言(如C)进行协作时用的,也就是native后的函数的实现不是用java写的。

·        native的意思就是通知操作系统,这个函数你必须给我实现,因为我要用。所以native关键字的函数就是操作系统实现的,java只能调用。

·        java是跨平台的语言,既然是跨平台,所付出的代价就是牺牲一些对底层的控制,而java要实现对底层的控制,就需要一些其他语言的帮助,这个就是native的作用了

 

Object的主要常用方法做个了解:

1public final nativeClass<?> getClass()方法

该方法也是native方法,返回的是该Object对象的类对象/运行时类对象。

类对象: 在java中,类是对具有一组相同特征或者行为的实例的抽象并进行描述,对象是该类所描述的特征或者行为的具体实例。作为概念层次的类,其本身也具有某些共同的属性,如都具有类型、类加载器、包、父类、属性、方法等。java中有专门定义一个类,Class,该类用于描述其他类所具有的这些特征。因此,从该角度来看,类本身也都是属于Class类的对象。 该部分涉及到反射的知识。

 

2 public native int hashCode()方法

hashCode返回的值:一般是通过将该对象的内存地址转换成一个整数来实现。该方法不是java实现的,因此使用了native关键字。

 

3 public boolean equals(Object obj)方法

有关equals()方法与==运算符的区别,前面的课程中有过讲解,但是在这里,还要进一步的讨论一个问题:重写equals方法时一定要重写hashCode方法,为什么?

我们知道在Object类中,hashCode方法是通过Object对象的地址计算出来的,因为Object对象只与自身相等,所以同一个对象的地址总是相等的,计算取得的哈希码也必然相等,对于不同的对象,由于地址不同,所获取的哈希码自然也不会相等。因此到这里我们就明白了,如果一个类重写了equals方法,但没有重写hashCode方法,将会直接违反重写equals方法必须要有对称性原则:对于任何非null的引用值,当x.equals(y)返回true时,y.equals(x)一定返回true的规定,这样的话,,就无法达到我们预期想要的效果。

用例子来说明:

对于s1s2的结果,我们并不惊讶,因为相同的内容的s1s2获取相同内的value这个很正常,因为String类重写了equals方法和hashCode方法,使其比较的是内容和获取的是内容的哈希码。

 

但是对于k1k2的结果就不太尽人意了,k1获取到的值是2k2获取到的是null,这是为什么呢?想必大家已经发现了,Key只重写了equals方法并没有重写hashCode方法,这样的话,equals比较的确实是内容,hashCode方法呢?没重写,那就肯定调用超类ObjecthashCode方法,这样返回的不就是地址了吗?k1k2属于两个不同的对象,返回的地址肯定不一样,所以现在我们知道调用map2.get(k2)为什么返回null了吧!

 

解决也很简单,重写hashCode方法,返回equals方法判断相等的关键属性的hashCode值:

记住结论:给一个类重写equals方法时一定要重写hashCode方法

 

3protected native Object clone( )

clone()方法又是一个被声明为native的方法,因此,我们知道了clone()方法并不是Java的原生方法,具体的实现是有C/C++完成

clone英文翻译为克隆,其目的是创建并返回此对象的一个副本。

Java术语表述为:clone函数返回的是一个引用,指向的是新的clone出来的对象,此对象与原对象分别占用不同的堆空间。

 

例子说明:

例子很简单,在main()方法中,new一个Oject对象后,想直接调用此对象的clone方法克隆一个对象,但是你会发现用不了!

 

why?回到Object类中clone()方法的定义,可以看到其被声明为protectedprotected修饰的属性或方法表示:在同一个包内或者不同包的子类可以访问。显然,Object类与ObjectTest类在不同的包中,但是ObjectTest继承自Object,是Object类的子类,于是,现在却出现子类中通过Object引用不能访问protected方法,原因在于对不同包中的子类可以访问没有正确理解。

 

不同包中的子类可以访问是指当两个类不在同一个包中的时候,继承自父类的子类内部且主调(调用者)为子类的引用时才能访问父类用protected修饰的成员(属性/方法)。 在子类内部,主调为父类的引用时并不能访问此protected修饰的成员

是的,因为此时的主调已经是子类的引用了。

上述代码在运行过程中会抛出”java.lang.CloneNotSupportedException”,表明clone()方法并未正确执行完毕,问题的原因在与Java中的语法规定:

clone()的正确调用是需要实现Cloneable接口,如果没有实现Cloneable接口,并且子类直接调用Object类的clone()方法,则会抛出CloneNotSupportedException异常。

Cloneable接口仅是一个标记接口,接口本身不包含任何方法,用来指示Object.clone()可以合法的被子类引用所调用。

于是,上述代码改成如下形式,即可正确指定clone()方法以实现克隆。

总结:

1. Obejct类的clone()方法实现的是浅拷贝

2. 在子类内部,主调为父类的引用时并不能访问此protected修饰的成员。调用者为子类的引用时才能访问父类中用protected修饰的成员

3. 想要在子类中调用父类的clone()方法,子类需要实现Cloneable接口,该接口用来指示Object.clone()可以合法的被子类引用的标记

4.简单谈谈什么时候需要用到clone方法呢,开发飞机大战的游戏,发射出来的子弹,每个子弹就是一个对象,这个时候就可以用clone方法复制子弹对象,它是一种浅拷贝,可以大量节约内存的开销。

5.clone()new的区别:

1)在javaclone()new都能创建对象。

2clone()不会调用构造方法;new会调用构造方法。

3clone()能快速创建一个已有对象的副本,即创建对象并且将已有对象中所有属性值克隆;new只能在JVM中申请一个空的内存区域,对象的属性值要通过构造方法赋值。

注意:

1)使用clone()类必须实现java.lang.Cloneable接口并重写Object类的clone()方法,如果没有实现Cloneable()接口将会抛出CloneNotSupportedException异常。(此类实现java.lang.Cloneable接口,指示Object.clone()方法可以合法的对该类实例进行按字段复制。)

2)默认的Object.clone()方法是浅拷贝,创建好对象的副本然后通过赋值拷贝内容,如果类包含引用类型变量,那么原始对象和克隆对象的引用将指向相同的引用内容。

面试题:什么是浅拷贝?什么是深拷贝?

浅拷贝:默认的Object.clone()方法,对于引用类型成员变量拷贝只是拷贝即地址,没有在堆中开辟新的内存空间。

深拷贝:重写clone()方法,拷贝一个对象时,不仅仅把对象的引用进行复制,还把该对象引用的值也一起拷贝,会在堆中开辟新的内存空间。

 

4public String toString()方法

当使用System.out.println(Object obj);时,返回的就是该obj对象的toString方法,实际上System.out.println()内部是通过toString实现的

getClass()返回对象的类对象,getClassName()String形式返回类对象的名称(含包名)。Integer.toHexString(hashCode())则是以对象的哈希码为实参,以16进制无符号整数形式返回此哈希码的字符串表示形式。

u1对象的哈希码是638,则对应的16进制为27e,调用toString()方法返回的结果为:com.corn.User@27e。包名.类名@哈希码

因此:toString()是由对象的类型和其哈希码唯一确定,同一类型但不相等的两个对象分别调用toString()方法返回的结果不可能相同,除非重写了toString()方法。

 

 

 

 

 

 

 

 

 

 

以上是关于第十六天的主要内容,如果未能解决你的问题,请参考以下文章

第十六天

第十六天休息

第十六天

第十六天

安卓第十六天笔记-音频与视频播放

第十六天