Object:超类
Object是Java默认提供的一个类。Java里面除了Object类,所有的类都是存在继承关系的。默认会继承Object父
类。即所有类的对象都可以使用Object的引用进行接收。
package One;
import java.util.Objects;
public class Date { //Date没有继承任何类但是使用关键字super调用Object,Object:超类,当一个类没有继承自任何类则默认继承Object
//Objecct没有基类
int year;
int month;
int day;
public Date(int year, int month, int day) {
super();//在构造方法中super关键字调用基类的构造方法
this.year = year;
this.month = month;
this.day = day;
}
@Override
public String toString() {
/*System.out.println(getClass().getName());
System.out.println(hashCode());
return super.toString();
*/
return "["+year+"-"+month+"-"+day+"]";
}
public static void method1(){
Date d1=new Date(1222,3,3);
System.out.println(d1);
}
public static void method2(){
int a=10;
int b=20;
int c=10;
//内置类型使用”=="比较
//"=="左右两端如果是内置类型的变量,直接用变量中的值进行比较
System.out.println(a==b);
System.out.println(a==c);
System.out.println("==========");
//自定义类型使用"=="比较
//d1和d3日期相同应该返回true,d2和d1不同应该返回false
//但是最终都返回了false,并不是将引用对象的内容进行比较
//而是将引用对象的地址进行比较
Date d1=new Date(1111,1,1);
Date d2=new Date(1222,2,2);
Date d3=new Date(1111,1,1);
System.out.println(d1==d2);
System.out.println(d1==d3);
//注意:如果想要比较内容,则需要调用equals方法来进行比较,但是一定要对Object中的equals方法进行重写
System.out.println("==============");
System.out.println(d1.equals(d2));
System.out.println(d1.equals(d3));
System.out.println(d1.equals(null));
}
@Override
public boolean equals(Object obj) { //重写equals方法不能对其中变量进行修改
//1、检查是否为空,如果为空则返回false
if(obj==null){
return false;
}
//2、this==obj:检测是否指向同一个对象,既然对象一样,则内容肯定一样,就没有必要去逐个比较了
if(this==obj){
return true;
}
//3、当两个不同的对象进行比较时:此时应该检测obj是否是当前一个类的对象,如果不是该类的对象则肯定不相等,返回false
if(!(obj instanceof Date)){
return false;
}
//4、说明:obj现在就是Date的一个对象,将obj强转为当前类的对象,然后使用对象中的成员逐个进行比较
Date d=(Date)obj;
return(year==d.year)&&
(month==d.month)&&
(day==d.day);
}
public static void method3(){
//两个对象内容相同,一般情况下,我们会让其哈希码也相同,但实际上不一样
Date d1=new Date(1111,1,1);
Date d3=new Date(1111,1,1);
System.out.println(d1.hashCode());
System.out.println(d3.hashCode());
//两个对象内容相同,要想让其哈希码相同,则我们就要对其Object中的hashCode()方法进行重写:用对象中的成员去计算hashCode
}
@Override
public int hashCode() {
return Objects.hash(year,month,day);
}
public static void main(String[] args) {
// method1();
// method2();
method3();
}
}
Clonable 接口和深拷贝
Java 中内置了一些很有用的接口, Clonable 就是其中之一.
Object 类中存在一个 clone 方法, 调用这个方法可以创建一个对象的 "拷贝". 但是要想合法调用 clone 方法, 必须要
先实现 Clonable 接口, 否则就会抛出 CloneNotSupportedException 异常.
package Two;
public class Date implements Cloneable{ //Date没有继承任何类但是使用关键字super调用Object,Object:超类,当一个类没有继承自任何类则默认继承Object
//Objecct没有基类
int year;
int month;
int day;
Time t;
public Date(int year, int month, int day,Time t) {
super();//在构造方法中super关键字调用基类的构造方法
this.year = year;
this.month = month;
this.day = day;
this.t=t;
}
@Override
public String toString() {
/*System.out.println(getClass().getName());
System.out.println(hashCode());
return super.toString();
*/
return "["+year+"-"+month+"-"+day+"]";
}
@Override
public boolean equals(Object obj) { //重写equals方法不能对其中变量进行修改
//1、检查是否为空,如果为空则返回false
if(obj==null){
return false;
}
//2、this==obj:检测是否指向同一个对象,既然对象一样,则内容肯定一样,就没有必要去逐个比较了
if(this==obj){
return true;
}
//3、当两个不同的对象进行比较时:此时应该检测obj是否是当前一个类的对象,如果不是该类的对象则肯定不相等,返回false
if(!(obj instanceof Date)){
return false;
}
//4、说明:obj现在就是Date的一个对象,将obj强转为当前类的对象,然后使用对象中的成员逐个进行比较
//内部包含类中也必需要重写equal方法
Date d=(Date)obj;
return(year==d.year)&&
(month==d.month)&&
(day==d.day)&&
(t.equals(d.t));
}
public static void method1(){
Two.Date d1=new Two.Date(1111,1,1,new Time(11,1,1));
Two.Date d3=new Two.Date(1111,1,1,new Time(11,1,1));
System.out.println(d1.equals(d3));
}
public static void method2(){
Two.Date d1=new Two.Date(1111,1,1,new Time(11,1,1));
Date d2=d1;
//浅拷贝:这种拷贝只是将对象的地址拷贝一份:即d1和d2指向同一个对象,引用对象时就会引用同一个对象,不可取
d1.day=4;
System.out.println(d1);
System.out.println(d2);
d2.day=5;
System.out.println(d1);
System.out.println(d2);
//这种方法并不是去拷贝对象只是多一个引用去共享对象
}
//对象的拷贝
public static void method3() throws CloneNotSupportedException {
Two.Date d1=new Two.Date(1111,1,1,new Time(11,1,1));
//将对象拷贝一份
Date d2=(Date)d1.clone();
System.out.println(d2);
//clone方法会抛出异常,简单处理异常:Alt+enter
//注意:一个类要想实现克隆方法必须要实现Cloneable接口
//表明:该类支持克隆,否则就会抛出异常
}
@Override
protected Date clone() throws CloneNotSupportedException {
Time newTime=new Time(t.hour,t.minute,t.second);
return new Date(year,month,day,new Time(2,3,4));
}
public static void main(String[] args) throws CloneNotSupportedException {
// method1();
// method2();
method3();
}
}
package Two;
public class Time implements Cloneable{
public int hour;
public int minute;
public int second;
public Time(int hour, int minute, int second) {
this.hour = hour;
this.minute = minute;
this.second = second;
}
@Override
public String toString() {
return "["+hour+"-"+minute+"-"+second+"]";
}
public static void method(){
Time t1=new Time(11,11,11);
Time t2=new Time(11,11,11);
}
@Override
public boolean equals(Object obj) {
if(obj==null){
return false;
}
if(this==obj){
return true;
}
if(!(obj instanceof Time)){
return false;
}
Time t1=(Time)obj;
return (hour==t1.hour)&&
(minute==t1.minute)&&
(second==t1.second);
}
public static void main(String[] args) throws CloneNotSupportedException {
Time t1=new Time(12,3,4);
Time t2=(Time)t1.clone();
System.out.println(t1);
System.out.println(t2);
t1.hour=5;
t2.minute=3;
System.out.println(t1);
System.out.println(t2);
//此时为深拷贝t1和t2的地址不同
//如果克隆对象都是内置成员类型的,此时不需要重写Clonable接口中的抽象方法
//该类可以直接使用Object中的Clone方法
}
}
再来重写创建一个Three包调用Two中的Time类
package Three;
import Two.Time;
public class Date implements Cloneable{ //Date没有继承任何类但是使用关键字super调用Object,Object:超类,当一个类没有继承自任何类则默认继承Object
//Objecct没有基类
int year;
int month;
int day;
Time t;
public Date(int year, int month, int day, Time t) {
this.year = year;
this.month = month;
this.day = day;
this.t=t;
}
@Override
public String toString() {
return "["+year+"-"+month+"-"+day+"-"+"]"+t;
}
//如果此时类中包含引用类型的成员变量,此时没有重写Object类中的Clone方法,但是继续使用clone接口
//如果此时包含引用类型的成员变量,拷贝结束之后,新对象和原对象中引用的成员变量都指向同一个实体
//一个实体将变量修改之后,另外一个实体也会改变,为了解决这个问题,可以利用深拷贝的方式来解决:
//1、可以重写Object类中Clone方法
//2、在重写clonef方法中,直接new新对象,将原对象中的每个成员放到新对象中
//注意如果新对象中也有引用类型变量,引用类型的对象也要重新创建一个
public static void method1() throws CloneNotSupportedException {
//深拷贝:Date重写Object中的clone方法
Date d=new Date(2,3,1,new Time(2,3,4));
Date d1=(Date)d.clone();
System.out.println(d);
System.out.println(d1);
d.t=new Time(1,3,4);
System.out.println(d);
System.out.println(d1);
}
//对object中的Clone类进行重写:返回值改为Date类,访问权限改为public,创建引用类型的一个新对象,返回Date
@Override
public Date clone() throws CloneNotSupportedException {
Time newTime=new Time(t.hour,t.minute,t.second);
return new Date(year,month,day,newTime);
}
/*public static void method2() throws CloneNotSupportedException {
//浅拷贝:Date类中没有重写Object中的Clone方法
Date d=new Date(2,3,1,new Time(2,3,4));
Date d1=(Date)d.clone();
System.out.println(d);
System.out.println(d1);
d.t=new Time(1,3,4);
System.out.println(d);
System.out.println(d1);
}
*/
public static void main(String[] args) throws CloneNotSupportedException {
method1();
//method2();
}
}
抽象类和接口都是 Java 中多态的常见使用方式. 都需要重点掌握. 同时又要认清两者的区别.
核心区别: 抽象类中可以包含普通方法和普通字段, 这样的普通方法和字段可以被子类直接使用(不必重写), 而接口中
不能包含普通方法, 子类必须重写所有的抽象方法.
如之前写的 Animal 例子. 此处的 Animal 中包含一个 name 这样的属性, 这个属性在任何子类中都是存在的. 因此此
处的 Animal 只能作为一个抽象类, 而不应该成为一个接口