方法的重写
在子类中如果创建了一个与父类中相同名称、相同返回值类型、相同参数列表的方法
只是方法体中的实现不同,以实现不同于父类的功能,扩展增强。
这种方式被称为方法重写(override),又称为方法覆盖。当父类中的方法无法满足子类需求或子类具有特有功能的时候,需要方法重写。
Object类
Java里所有的类都有一个共同的父类Object,不管你愿不愿意都得继承他(默认继承,不用加extends)
此外,还有许多在这些类上+s的比如Objects类,表示是Object的工具类 备胎男
所以,如果你不重写方法(比如equals,toString等等)就会默认调用Object类里面的方法,产生不一样的结果!
比如Obj类里面的equals和String类里面的equals是不一样的!
toString类
用于输出类的值
在jdk帮助文档中给出明确介绍,会返回一个
getClass().getName() + '@' + Integer.toHexString(hashCode())
重写:
alt+ins 选择toString()即可
这里idea会让你选择输出类的属性,比如一个学生类,选择输出id,name,sex,里面的一种或几种
@Override public String toString() { return "User{" + "uname='" + uname + '\'' + ", password='" + password + '\'' + '}'; }
hashcode类
C语言实现 没有源代码
为不同的对象返回不同的整数int 有些时候obj的hashcode就是你在内存中的地址(如果不重写)
如果重写 hashcode禁止相同!!确定对象的唯一性
每次重写hashcode的时候都会绑定一个equals方法
@Override public int hashCode() { return Objects.hash(id, name);
equals类
如何比较两个对象 基本数据类型:值 引用数据类型:内存地址
== 比较地址 指针操作 Obj类里面equals比较的是值 源码用的还是==
case1
当我们遇到这种情况,比较两个对象是否相等?对象有很多元素,比如id,name,date,age...但是我们判断的唯一标准选择id即可。这时需要重写equals类。
@Override public boolean equals(Object o) { if (this == o) return true;//先比较对象的内存地址是否相同 if (o == null || getClass() != o.getClass()) return false;//对象是不是空,两个对象在不在一个类里面 Star star = (Star) o;//强转obj=>star return idcard == star.idcard; } @Override public int hashCode() { return Objects.hash(idcard, name, info); }
上面这个代码建议把hashcode里面的参数和euqals保持一致 夫妻一体同心
case2
当然,如果要比较的对象是组合关系,比如下面这段。必须!!!重写自定义属性类的equals方法。否则就默认调用obj的equals(==)
public static void main(String[] args) { Computer mac = new Computer(111,"mac", new Cpu(11,"m1")); Computer win = new Computer(111,"win", new Cpu(11,"i7")); //内存地址不等 System.out.println(mac==win); //属性的值 System.out.println(mac.equals(win)); }
Computer类里的元素包含了Cpu类
class Computer{ public int id; public String name; public Cpu cpu; public Computer(int id, String name, Cpu cpu) { this.id = id; this.name = name; this.cpu = cpu; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Computer computer = (Computer) o; return id == computer.id && Objects.equals(cpu, computer.cpu);//调用了cpu类的equals 如果不重写就调用obj的 //int类型== //string类型equals str类重写 } @Override public int hashCode() { return Objects.hash(id, cpu); } }
cpu类
class Cpu{ public int id; public String name; public Cpu(int id, String name) { this.id = id; this.name = name; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Cpu cpu = (Cpu) o; return id == cpu.id; } @Override public int hashCode() { return Objects.hash(id); } }
case3
如果一个类有1000个属性。有8个对象,如何找到两个相等的呢?
答:先比较hashcode,再equals
这俩是夫妻 hashcode具有假唯一性(大部分的元素hashcode都不想等 但是不排除有相等的情况)
盒子原理:把三个苹果放到两个盒子中要求每个盒子至少有一个苹果 那么肯定有一个盒子中装了两个苹果
所以hashcode和euqals的重写是一起的,并且equals选择了哪些元素,hashcode就一定选择哪些(当然,也可以比equals少,equals更严格)
即只要equals了他们的hashcode一定相等!!! 反过来不成立
题外话——类的构造函数
快捷键:alt+ins
此时会自动生成一个有参的构造函数,在主函数里面给对象的属性赋值的时候就会自动帮你完成一些初始化操作。
如果没有再定义一个无参构造函数的话上面new对象的代码会报错。
Teacher lisa = new Teacher(111, "lisa"); Teacher bob = new Teacher(111, "bob");
如果没有定义有参构造,这样写即可
Teacher lisa = new Teacher();
但是根据秦老师说的如果写了有参构造,就必须把无参构造也写去!!!
这样new的时候怎么写都不会报错啦~
clone类
浅克隆
首先借助Object.clone()方法
克隆类要实现Cloneable接口,但他是一个标记接口(空的)
重写Object.clone()方法,提高访问权限,把protected改成public
对引用的克隆对象进行转型
抛出CloneNotSupportedException异常或
class Desk implements Cloneable{ public int id; public String name; public Desk(int id, String name) { this.id = id; this.name = name; } @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } @Override public String toString() { return "Desk{" + "id=" + id + ", name='" + name + '\'' + '}'; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Desk desk = (Desk) o; return id == desk.id && Objects.equals(name, desk.name); } @Override public int hashCode() { return Objects.hash(id, name); } }
主函数
public static void main(String[] args) throws CloneNotSupportedException{//抛出异常 Desk desk1 = new Desk(1,"blues"); //desk2的引用指向了desk1 //Desk desk2 = desk1; Desk desk2= (Desk) desk1.clone();//转型 System.out.println(desk2==desk1);//虽然是克隆的 副本 但是地址不一样 ==比较地址 System.out.println(desk2.equals(desk1));//重写后是true }
深克隆
深克隆方法:
浅克隆的他都要有,本质就是对克隆对象的子类对象也进行一遍克隆,实际上也是一样的操作
对子类也implements cloneable
重写clone函数改成public
再修改一下总对象(小汽车)的clone方法
新建一对象(即克隆后的小汽车)将此对象的属性也都克隆
class car implements Cloneable{ public int id; public Engine engine; public Tire tire; public car(int id, Engine engine, Tire tire) { this.id = id; this.engine = engine; this.tire = tire; } public car(){ } @Override public Object clone() throws CloneNotSupportedException { //deepc是被克隆对象 car deepc=(car) super.clone();//super是基类 //engine的深度克隆 deepc.engine= (Engine) this.engine.clone();//克隆完返回的是obj,需要强转 this本类 //tire的深度克隆 deepc.tire= (Tire) this.tire.clone(); return deepc; } @Override public String toString() { return "car{" + "id=" + id + ", engine=" + engine + ", tire=" + tire + '}'; } } class Engine implements Cloneable{ public int eid; public String ename; public Engine(int eid, String ename) { this.eid = eid; this.ename = ename; } public Engine() { } @Override public String toString() { return "Engine{" + "eid=" + eid + ", ename='" + ename + '\'' + '}'; } @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } } class Tire implements Cloneable{ public int tid; public String tname; public Tire(int tid, String tname) { this.tid = tid; this.tname = tname; } public Tire() { } @Override public Object clone() throws CloneNotSupportedException { return super.clone(); }
主函数
public static void main(String[] args) throws CloneNotSupportedException{ car car1 = new car(11,new Engine(221,"v8"),new Tire(331,"mql")); car car2= (car) car1.clone(); System.out.println(car1.toString()); System.out.println(car2.toString()); System.out.println("==========="); car1.engine.ename="ved"; System.out.println(car1.toString()); System.out.println(car2.toString()); }
包装类
Java不是完全面向对象的语言,因为他有一些基本数据类型。
在设计类的时候为每一个基本数据类型设计了一个对应的类代表
包装类的名字基本和原来类型一致,除了int和char
final修饰的类 太监类 不能被继承 里面的方法也不能重写