基本介绍
方法覆盖(重写)就是子类有一个方法,和父类的某个方法的名称、返回类型、参数一样,那么我们就说子类的这个方法覆盖了父类的方法。
使用案例
public class Animal { public void cry() { System.out.println("动物叫唤.."); } } public class Dog extends Animal{ //1. 因为 Dog 是 Animal 子类 //2. Dog 的 cry 方法和 Animal 的 cry 定义形式一样(名称、返回类型、参数) //3. 这时我们就说 Dog 的 cry 方法,重写了 Animal 的 cry 方法 public void cry() { System.out.println("小狗汪汪叫.."); } }
注意事项
1)子类的方法的方法名称,形参列表,要和父类方法的方法名称,形参列表完全一样。 2)子类方法的返回类型和父类方法返回类型一样,或者是父类返回类型的子类 比如:父类返回类型为Object,子类返回类型是String Public Object getInfo() { } public String getInfo() { } 3)子类方法不能缩小父类方法的访问权限 public > protected > 默认 > private
重写(override)和重载(overload)的区别
名称 | 发生范围 | 方法名 | 形参列表 | 返回类型 | 修饰符 |
---|---|---|---|---|---|
重载(overload) | 本类 | 必须一样 | 类型、个数或者顺序至少有一个不同 | 无要求 | 无要求 |
重写(override) | 父子类 | 必须一样 | 相同 | 子类方法不能缩小父类方法的访问范围 |
基本介绍
方法或对象具有多种形态。是面向对象的第三大特征,多态是建立在封装和继承基础之上的。
多态的具体体现
1)方法的多态
public class PloyMethod { public static void main(String[] args) { //方法重载体现多态 A a = new A(); //这里我们传入不同的参数,就会调用不同 sum 方法,就体现多态 System.out.println(a.sum(10, 20)); System.out.println(a.sum(10, 20, 30)); //方法重写体现多态 B b = new B(); a.say(); b.say(); } } class B { //父类 public void say() { System.out.println("B say() 方法被调用..."); } } class A extends B {//子类 public int sum(int n1, int n2){ //和下面 sum 构成重载 return n1 + n2; } public int sum(int n1, int n2, int n3){ return n1 + n2 + n3; } public void say() { System.out.println("Asay() 方法被调用..."); } } //结论:重写和重载就体现多态
2)对象的多态 1 - 一个对象的编译类型和运行类型可以不一致 2 - 编译类型在定义对象时,就确定了,不能被改变 3 - 运行类型是可以变化的 4 - 编译类型看定义时 =号的左边,运行类型看 =号的右边 比如: Animal animal = new Dog(); //animal的编译类型是Animal,运行类型是Dog animal = new Cat(); //animal的运行类型变成了Cat,编译类型仍然是Animal 补充:编译类型和运行类型 例如:Person person = new Student(); 这行代码将会生成一个person变量,该变量的编译时类型是Person,运行时类型是Student 类在继承时,子类会覆盖与父类相同的属性 总结一点就是:对象访问变量看声明,访问方法看实际对象类型(new出来的类型) java允许把一个子类对象直接赋值给一个父类引用变量,无须任何类型转换 或者被称为向上转型,由系统自动完成
//例: 在一个主人类master中的喂食方法 Public void feed(Dog dog, Bone bone) { System.out.println(this.name + “给” + dog.getName() + “eat” + bone.getName()); } Public void feed(Cat cat, Fish fish) { System.out.println(this.name + “给” + cat.getName() + “eat” + fish.getName()); } //为了提高代码的复用性,减少冗余代码量,可使用以下方法代替: Public void feed(Animal animal, Food food) { System.out.println(this.name + “给” + animal.getName() + “eat” + food.getName()); } //利用多态的原理: //animal变量的编译类型是Animal,可以指向(接收)Animal子类的对象 //food变量的编译类型是Food,可以指向(接收)Food子类的对象
多态注意事项
多态的前提是:两个对象(类)存在继承关系
多态的向上转型
1)本质:父类的引用指向了子类的对象 2)语法: 父类类型 引用名 = new 子类类型(); 3)特点:编译类型看左边,运行类型看右边。
//向上转型: 父类的引用 指向了 子类的对象 //语法:父类类型 引用名 = new 子类类型(); Animal animal = new Cat(); Object obj = new Cat();// Object 也是 Cat 的父类 //向上转型调用方法的规则如下: //(1) 可以调用父类中的所有成员(需遵守访问权限) //(2) 但是不能调用子类的特有的成员 //(3) 因为在编译阶段,能调用哪些成员,是由编译类型来决定的 //animal.catchMouse();错误 //(4) 最终运行效果看子类(运行类型)的具体实现,即调用方法时,按照从子类(运行类型)开始查找方法 //,然后调用,规则与前面讲的方法调用规则一致。
多态的向下转型
1)语法: 子类类型 引用名 = (子类类型)父类引用; 2)只能强转父类的引用,不能强转父类的对象 3)要求父类的引用必须指向的是当前目标类型的对象 4)当向下转型后,可以调用子类类型中所有的成员
//多态的向下转型 //(1) 语法:子类类型 引用名 =(子类类型)父类引用; //(2) 要求父类的引用必须指向的是当前目标类型的对象 //cat 的编译类型 Cat,运行类型是 Cat Animal animal1 = new Cat(); Cat cat = (Cat) animal1; //dog 的编译类型 Dog,运行类型是 Dog Animal animal2 = new Dog(); Dog dog = (Dog) animal2;
补充:
属性没有重写之说!属性的值看编译类型 instanceOf 比较操作符,用于判断对象的运行类型是否为 XX 类型 或 XX 类型的子类型
class AA {} //父类 class BB extends AA {}//子类 BB bb = new BB(); System.out.println(bb instanceof BB);// true System.out.println(bb instanceof AA);// true //aa 编译类型 AA, 运行类型是 BB //BB 是 AA 子类 AA aa = new BB(); System.out.println(aa instanceof AA); // true System.out.println(aa instanceof BB); // true Object obj = new Object(); System.out.println(obj instanceof AA);//false String str = "hello"; //System.out.println(str instanceof AA); System.out.println(str instanceof Object);//true
Java 的动态绑定机制
1)调用对象方法,方法会和该对象的 运行类型 / 内存地址 绑定. 2)调用对象属性,没有动态绑定机制,哪里声明,使用哪里的属性。
多态的应用
// 应用实例:现有一个继承结构如下:要求创建 1 个 Person 对象、 // 让 Student 和 Teacher 都继承 Person。 // 2 个 Student 对象和 2 个 Teacher 对象, 统一放在数组中,并调用每个对象 say 方法 Person[] persons = new Person[5]; persons[0] = new Person("jack", 20); persons[1] = new Student("mary", 18, 100); persons[2] = new Student("smith", 19, 30.1); persons[3] = new Teacher("scott", 30, 20000); persons[4] = new Teacher("king", 50, 25000);
equals 方法
== 和 equals 的对比 == 是一个比较运算符 1)既可以判断基本类型,又可以判断引用类型 2)如果判断基本类型,判断的是 值是否相等 示例: int i = 10; double d = 10.0; 3)如果判断引用类型,判断的是 地址是否相等 即判断是不是同一个对象 equals 是 Object类中的方法 1)equals 只能判断引用类型 2)默认判断的是地址是否相等,子类中往往重写该方法 用于判断内容是否相等 比如Integer,String
String类 重写equals方法源码
/* //看看 Jdk 的源码,String 类的 equals 方法 //把 Object 的 equals 方法重写了,变成了比较两个字符串值是否相同 */ public boolean equals(Object anObject) { if (this == anObject) { //如果是同一个对象 return true;//返回 true } if (anObject instanceof String) { //判断类型 String anotherString = (String)anObject; //向下转型 int n = value.length; if (n == anotherString.value.length) { //如果长度相同 char v1[] = value; char v2[] = anotherString.value; int i = 0; while (n-- != 0) { //然后一个一个的比较字符 if (v1[i] != v2[i]) return false; i++; } return true; //如果两个字符串的所有字符都相等,则返回 true } } return false; //如果比较的不是字符串,则直接返回 false }
Object类 equals方法源码
//即 Object类的 equals 方法默认就是比较对象地址是否相同 //也就是判断两个对象是不是同一个对象. public boolean equals(Object obj) { return (this == obj); }
Integer类 重写equals方法源码
//从源码可以看到 Integer类 也重写了 Object类的 equals 方法 //变成了判断两个值是否相同 public boolean equals(Object obj) { if (obj instanceof Integer) { return value == ((Integer)obj).intValue(); } return false; }
尝试自己重写equals
假如我们有一个Person对象,有name,age,gender属性,只需要这三个值相同,那么我们就认为这个对象相等。
//重写 Object 的 equals 方法 public boolean equals(Object obj) { //判断如果比较的两个对象是同一个对象,则直接返回 true if(this == obj) { return true; } //类型判断 if(obj instanceof Person) {//是 Person,我们才比较 //进行 向下转型, 因为我需要得到 obj 的 各个属性 Person p = (Person)obj; return this.name.equals(p.name) && this.age == p.age && this.gender == p.gender; } //如果不是 Person ,则直接返回 false return false; }
基本介绍
1)提高具有哈希结构的容器的效率。 2)两个引用,如果指向的是同一个对象,则哈希值肯定是一样的。 3)两个引用,如果指向的是不同对象,则哈希值是不一样的。 4)哈希值主要根据地址号来的!,但不能完全将哈希值等价于地址。 5)后面在集合中,如果需要 hashCode 的话,也会重写。
public class HashCode_ { public static void main(String[] args) { AA aa = new AA(); AA aa2 = new AA(); AA aa3 = aa; System.out.println("aa.hashCode()=" + aa.hashCode()); System.out.println("aa2.hashCode()=" + aa2.hashCode()); System.out.println("aa3.hashCode()=" + aa3.hashCode()); } } class AA {}
基本介绍
1)默认返回:全类名+@+哈希值的十六进制 【查看 Object 的 toString 方法】 2)子类往往重写 toString 方法,用于返回对象的属性信息。 3)重写 toString 方法,打印对象或拼接对象时,都会自动调用该对象的 toString 形式。 4)当直接输出一个对象时,toString 方法会被默认的调用。 比如System.out.println(monster); 就会默认调用 monster.toString()
Object 的 toString() 源码
//(1)getClass().getName() 类的全类名(包名+类名) //(2)Integer.toHexString(hashCode()) 将对象的 hashCode 值转成 16 进制字符串 public String toString() { return getClass().getName() + "@" + Integer.toHexString(hashCode()); }
基本介绍
当对象被回收时,系统自动调用该对象的 finalize 方法。子类可以重写该方法,做一些释放资源的操作。
什么时候被回收:当某个对象没有任何引用时,则 jvm 就认为这个对象是一个垃圾对象,就会使用垃圾回收机制来 销毁该对象,在销毁该对象前,会先调用 finalize 方法。
垃圾回收机制的调用,是由系统来决定(即有自己的 GC 算法), 也可以通过 System.gc() 主动触发垃圾回收机制。
//重写 finalize @Override protected void finalize() throws Throwable { System.out.println("我们销毁 汽车" + name); System.out.println("释放了某些资源..."); }
注:本博客引用韩顺平老师Java课程