【Java】面向对象(中级) - 2

6. 多态

6.1 例子引出多态话题

Animal 类

public class Animal {
	private String name;

	public Animal(String name) {
		this.name = name;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
}

Dog 类(继承Animal 类)

public class Dog extends Animal {	
	public Dog(String name) {
		super(name);
	}	
}

Cat 类(继承Animal 类)

public class Cat extends Animal {
	public Cat(String name) {
		super(name);
	}	
}

Food 类

public class Food {
	private String name;

	public Food(String name) {
		this.name = name;
	}

	public String  getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
}

Bone 类(继承 Food 类)

public class Bone extends Food {
	public Bone(String name) {
		super(name);
	}
}

Fish 类(继承 Food 类)

public class Fish extends Food  {
	public Fish(String name) {
		super(name);
	}
}

Master 类

public class Master {
	private String name;

	public Master(String name) {
		this.name = name;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	//主人给小狗 喂食 骨头
	public void feed(Dog dog, Bone bone) {
		System.out.println("主人 " + name + " 给 " + dog.getName() + " 吃 " + bone.getName());
	}

	//主人给小猫 喂食 鱼
	public void feed(Cat cat, Fish fish) {
		System.out.println("主人 " + name + " 给 " + cat.getName() + " 吃 " + fish.getName());
	}
}

Poly 类(main)

public class Poly {
	public static void main(String[] args) {
		Master tom = new Master("汤姆");
		Dog dog = new Dog("中华田园犬");
		Bone bone = new Bone("骨头");

		tom.feed(dog, bone);

		System.out.println("===== 0_0 =====");
		Cat cat = new Cat("小花猫");
		Fish fish = new Fish("鱼");

		tom.feed(cat, fish);
	}
}

如果动物很多,食物很多

feed方法很多,不利于管理和维护

问题: 代码的复用性不高,而且不利于代码维护

解决: 利用多态

6.2 多[多种]态[状态]基本介绍

方法或对象具有多种形态

是面向对象的第三大特征,多态是建立在封装和继承基础之上的

方法的多态

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;
	}

	public void say() {
		System.out.println("A say() 方法被调用");
	}	
}

对象的多态

(面向对象的核心)

  • 一个对象的编译类型和运行类型可以不一致
  • 编译类型在定义对象时,就确定了,不能改变
  • 运行类型是可以变化的
  • 编译类型看定义时 = 号的左边,运行类型看 = 号的右边
Animal animal = new Dog();	//【 animal 编译类型时Animal,运行类型时 Dog 】
animal = new Cat();			//【 animal 运行类型变成了 Cat,编译类型仍然时 Animal 】

在这里插入图片描述

public class Animal {
	public void cry() {
		System.out.println("Animal cry() 动物在叫...");
	}
}
public class Dog extends Animal {
	public void cry() {
		System.out.println("Dog cry() 小狗汪汪叫...");
	}
}
public class Cat extends Animal {
	public void cry() {
		System.out.println("Cat cry() 小猫喵喵叫...");
	}
}
public class PolyObject {
	public static void main(String[] args) {
		//体验对象多态特点
		Animal animal = new Dog();	//animal 编译类型时Animal,运行类型时 Dog
		
		animal.cry();	//小狗汪汪叫
		//运行时,执行该行时,animal运行类型时Dog,所以 cry 就是 Dog 的 cry

		//animal 运行类型变成了 Cat,编译类型仍然时 Animal
		animal = new Cat();
		animal.cry();	//小猫喵喵叫
	}	
}

多态基本学习

6.1 的例子进行改进,添加 Pig 类 和 Rice 类

并在 Master 类改进,利用多态使得用一个方法将所有的 动物类 和 食物类 的行为统一实现

Pig 类(继承 Animal 类)

public class Pig extends Animal {
	public Pig(String name) {
		super(name);
	}
}

Rice 类(继承 Food 类)

public class Rice extends Food {
	public Rice(String name) {
		super(name);
	}
}

Master 类

public class Master {
	private String name;

	public Master(String name) {
		this.name = name;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	//使用多态机制,可以同意的管理主人喂食的问题
	//animal 编译类型是Animal,可以指向(接收)Animal子类的对象
	//food 编译类型是 Food,可以指向(接收) Food子类对象
	public void feed(Animal animal, Food food) {
		System.out.println("主人 " + name + " 给 " + animal.getName() + " 吃 " + food.getName());
	}

	//主人给小狗 喂食 骨头
	//public void feed(Dog dog, Bone bone) {
	//	System.out.println("主人 " + name + " 给 " + dog.getName() + " 吃 " + bone.getName());
	//}

	//主人给小猫 喂食 鱼
	//public void feed(Cat cat, Fish fish) {
	//	System.out.println("主人 " + name + " 给 " + cat.getName() + " 吃 " + fish.getName());
	//}
}

Poly 类

public class Poly {
	public static void main(String[] args) {
		Master tom = new Master("汤姆");
		Dog dog = new Dog("中华田园犬");
		Bone bone = new Bone("骨头");

		tom.feed(dog, bone);

		System.out.println("===== 0_0 =====");
		Cat cat = new Cat("小花猫");
		Fish fish = new Fish("鱼");

		tom.feed(cat, fish);
	
		System.out.println("===== 0_0 =====");
		//添加 给小猪喂米饭
		Pig pig = new Pig("小花猪");
		Rice rice = new Rice("米饭");
		tom.feed(pig, rice);
	}
}

只用一个方法就可以实现多种 动物类 和 食物类 的动作

在这里插入图片描述

6.3 向上转型

多态的前提是:两个对象(类)存在继承关系

多态的向上转型

  1. 本质:父类的引用指向了子类的对象
  2. 语法:父类类型 引用名 = new 子类类型();
  3. 特点:编译类型看左边,运行类型看右边。
    1. 可以调用父类中的所有成员(需遵守访问权限),
    2. 不能调用子类中特有成员;
    3. 最终运行效果看子类的具体实现

Animal 类

public class Animal {
	String name = "动物";
	int age = 10;

	public void sleep() {
		System.out.println("睡觉");
	}

	public void run() {
		System.out.println("跑步");
	}

	public void eat() {
		System.out.println("吃饭");
	}

	public void show() {
		System.out.println("hello,你好");
	}
}

Cat 继承 Animal 类

public class Cat extends Animal {
	public void eat() {	//方法重写
		System.out.println("猫吃鱼");
	}

	public void catchMouse() {	//Cat 特有方法
		System.out.println("猫抓老鼠");
	}
}
public class Poly {
	public static void main(String[] args) {
		//向上转型:父类的引用指向了子类的对象
		//语法:引用名 = new 子类类型();
		Animal animal = new Cat();
		Object obj = new Cat();	//可以,Object 也是 Cat 的父类
		
		//向上转型调用方法的规则如下:	
		//1.可以调用父类中的所有成员(需遵守访问权限)
		//2.但是不能调用子类的特有的成员
		//2.因为在编译阶段,能调用哪些成员,是山编译类型来决定的
		//animal.catchMouse();错误
		//4.最终运行效果看子类的具体实现,最终运行效果看子类的具体实现,即调用方法时,按照从子类开始查找方法
		//然后调用,规则和方法调用规则一致
		animal.eat();	//猫吃鱼
		animal.run();	//跑步
		animal.show();	//hello,你好
		animal.sleep();	//睡觉
			
		System.out.println("ok~");
	}
}

6.4 向下转型

多态的向下转型

  1. 语法:子类类型 引用名 = (子类类型)父类引用;
  2. 只能强转父类的引用,不能强转父类的对象
  3. 要求父类的引用必须指向的是当前目标类型的对象
  4. 当向下转型后,可以调用子类类型中所有的成员

在这里插入图片描述

public class Dog extends Animal {	//Dog类 继承 Animal 类

}
public class Poly {
	public static void main(String[] args) {
		//向上转型:父类的引用指向了子类的对象
		//语法:引用名 = new 子类类型();
		Animal animal = new Cat();
		Object obj = new Cat();	//可以,Object 也是 Cat 的父类
		
		//父类 Animal 可以调用子类 Cat 的 catchMouse 方法
		//利用多态的向下转型
		//
		//1.语法:子类类型	引用名 = (子类类型)父类引用;
		//
		//cat 的编译类型时 Cat,运行类型时 Cat
		Cat cat = (Cat)animal;
		cat.catchMouse();	//猫抓老鼠 
		//2.要求父类的引用必须指向的是当前目标类型的对象
		//
		//Dog dog = (Dog)animal;	//问题:可以这样写吗?
		//不可以,因为 Dog 类 和 Cat类 不相关,不可以将猫强制转换成一只狗
		//编译不报错,运行代码,会报错
		
		System.out.println("ok~");
	}
}

6.5 属性重写问题

属性没有重写之说!属性的值看编译类型

public class Poly {
        public static void main(String[] args) {
                //属性没有重写之说!属性的值看编译类型
                Base base = new Sub();
                System.out.println(base.count); //输出的是?看编译类型,输出10
                Sub sub = new Sub();
                System.out.println(sub.count);  //输出的是?看编译类型,输出20
        }
}

class Base {    //父类
        int count = 10; //属性
}

class Sub extends Base {        //子类
        int count = 20; //属性
}

instanceOf 比较操作符,用于判断对象的运行类型是否为XX类型或XX类型的子类型

public class Poly02 {
	public static void main(String[] args) {
		BB bb = new BB();
		System.out.println(bb instanceof BB);	//输出的时真还是假?
		//true

		System.out.println(bb instanceof AA);	//输出的时真还是假?	
		//true

		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
	}
}

class AA {}	//父类

class BB extends AA {}	//子类

6.6 练习

  • 查看代码是否正确?
public class PolyExercise01 {
	public static void main(String[] args) {
		double d = 3.14;
		long l = (long)d;	//正确
		System.out.println(l);	//13

		int in = 5;	//正确
		//boolean b = (boolean)in;	//不对, booolean -> int
		
		Object obj = "hello";	//可以,向上转型
		String objStr = (String)obj;	//可以,向下转型
		System.out.println(objStr);	//hello

		Object objPri = new Integer(5);	//可以,向上转型
		//String str = (String)objPro;	//错误ClassCastException,指向Integer的父类引用,转成String
		Integer str1 = (Integer)objPri;	//可以,向下转型
	}
}
  • 查看代码运行输出的是什么?
public class PolyExercise02 {
	public static void main(String[] args) {
		Sub s = new Sub();
		System.out.println(s.count);	//20
		s.display();	//20

		Base b = s;
		System.out.println(b == s);	//true
		System.out.println(b.count);	//10
		b.display();	//20
	}
}

class Base {	//父类
	int count = 10;
	public void display() {
		System.out.println(this.count);
	}
}

class Sub extends Base {	//子类
	int count = 20;
	public void display() {
		System.out.println(this.count);
	}
}

6.7 Java 的动态绑定机制

Java重要特性:动态绑定机制

java 的动态绑定机制

  1. 当调用对象方法的时候,该方法会和该对象的内存地址/运行类型绑定
  2. 当调用对象属性时,没有动态绑定机制,哪里声明,那里使用
public class DynamicBinding {
	public static void main(String[] args) {
		A a = new B();	//向上转型
		System.out.println(a.sum());	//没注释前的输出的是?	40
		//把子类的 sum() 方法注释后,输出的是?
		//a 调用 sum() 方法,发现子类没有,
		//因为继承机制,去父类找
		//所以去子类 B找 sum() 方法
		//但是子类的 sum() 方法被注释了
		//因此到父类找到 sum() 方法
		//但是 sum() 方法调用了 getI() 方法
		//问题是 父类 和 子类 都有 getI() 方法
		//现在该调用什么 父类 还是 子类的 getI() 方法
		//
		//动态绑定机制解决这个问题
		//注意点:当调用对象方法的时候,该方法会和该对象的内存地址/运行类型绑定
		//
		//因为 a 的编译类型是 A,运行类型是 B
		//所以到 父类 的 sum() 方法时,因为动态绑定机制,父类的sum() 方法去调用 子类 的 getI() 方法
		//
		//所以注释后的输出应该是 20 + 10 = 30

		System.out.println(a.sum1());	//没注释前的输出的是?	30
		//把子类的 sum1() 方法注释后,输出的是?
		//
		//注意点:当调用对象属性时,没有动态绑定机制,哪里声明,那里使用
		//
		//所以子类没有sum1()
		//去调用父类的 sum1()
		//因为对象属性 i 时没有动态绑定机制,
		//所以调用的时 父类的 i
		//
		//所以注释后的输出应该时 10 + 10 = 20
	}
}

class A {	//父类
	public int i = 10;
	public int sum() {
		return getI() + 10;
	}

	public int sum1() {
		return i + 10;
	}

	public int getI() {
		return i;
	}
}

class B extends A {	//子类
	public int i = 20;
	
	//public int sum() {
	//	return i + 20;
	//}

	public int getI() {
		return i;
	}

	//public int sum1(){
	//	return i + 10;
	//}
}

6.8 多态的应用

数组的定义类型为父类类型,里面保存的实际元素类型为子类类型

应用实例

现有一个继承结构如下:要求创建1个Person对象、2个Student对象和2个 Teacher 对象,统一放在数组中,并调用每个对象say方法

Person 父类

public class Person {	//父类
	private String name;
	private int age;

	public Person(String name, int age) {
		this.name = name;
		this.age = age;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}
	
	public String say() {	//返回名字和年龄
		return name + "\t" + age;
	}
}

Student 子类

public class Student extends Person {
	private double score;

	public Student(String name, int age, double score) {
		super(name,age);
		this.score = score;
	}

	public double getScore() {
		return score;
	}

	public void setScore(double score) {
		this.score = score;
	}

	//重写父类 say()
	public String say() {
		return "老师 " + super.say() + " score = " + score;
	}
}

Teacher 子类

public class Teacher extends Person {
	private double salary;

	public Teacher(String name, int age, double salary) {
		super(name, age);
		this.salary = salary;
	}

	public double getSalary() {
		return salary;
	}

	public void setSalary(double salary) {
		this.salary = salary;
	}

	//重写父类的方法
	public String say() {
		return "学生 " + super.say() + " salary = " + salary;
	}	
}

PolyArry

public class PolyArry {
	public static void main(String[] args) {
		//应用实例:
		//
		//现有一个继承结构如下:要求创建1个Person对象、2个Student对象和2个  Teacher 对象,统一放在数组中,并调用每个对象say方法
		Person[] persons = new Person[5];
		persons[0] = new Person("jack", 20);
		persons[1] = new Student("jack", 18, 100);
		persons[2] = new Student("smith", 19, 30.1);
		persons[3] = new Teacher("scott", 30, 20000);
		persons[4] = new Teacher("king", 50, 25000);

		//循环遍历多态数组,调用 say()
		for (int i = 0; i < persons.length; i++) {
			//person[i]编译类型是 Person ,运行类型是是根据实际情况有JVM来判断
			System.out.println(persons[i].say());	//动态绑定机制,会根据绑定的 运行类型 而调用 相关的 say() 方法
		}
	}
}

应用实例升级:

如何调用子类特有的方法,比如Teacher有一个 teach , Student有一个study 怎么调用?

Teacher 类

public class Teacher extends Person {
	private double salary;

	public Teacher(String name, int age, double salary) {
		super(name, age);
		this.salary = salary;
	}

	public double getSalary() {
		return salary;
	}

	public void setSalary(double salary) {
		this.salary = salary;
	}

	//重写父类的方法
	public String say() {
		return "老师 " + super.say() + " salary = " + salary;
	}
	
	//特有方法
	public void teach() {
		System.out.println("老师 " + getName() + " 正在讲java课程...");
	} 	
}

Student 类

public class Student extends Person {
	private double score;

	public Student(String name, int age, double score) {
		super(name,age);
		this.score = score;
	}

	public double getScore() {
		return score;
	}

	public void setScore(double score) {
		this.score = score;
	}

	//重写父类 say()
	public String say() {
		return "学生 " + super.say() + " score = " + score;
	}

	//特有方法
	public void study() {
		System.out.println("学生 " + getName() + " 正在学习...");	
	}
}

PolyArry 类

public class PolyArry {
	public static void main(String[] args) {
		//应用实例:
		//
		//现有一个继承结构如下:要求创建1个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);

		//循环遍历多态数组,调用 say()
		for (int i = 0; i < persons.length; i++) {
			//person[i]编译类型是 Person ,运行类型是是根据实际情况有JVM来判断
			System.out.println(persons[i].say());	//动态绑定机制,会根据绑定的 运行类型 而调用 相关的 say() 方法
		
			//使用 类型判断 + 向下转型	
			if (persons[i] instanceof Student) {	//判断person[i] 的运行类型是不是Student
				Student student = (Student)persons[i];	//向下转型
				student.study();
				//
				//也可以直接使用一条语句
				//(Student)persons[i].study();
			} else if (persons[i] instanceof Teacher) {
				Teacher teacher = (Teacher)persons[i];
				teacher.teach();
			} else if (persons[i] instanceof Person) {
				//不做任何操作
			} else {
				System.out.println("你的类型有误,请检查...");
			}
		}
	}
}

代码的维护性更强

6.9 多态参数

多态参数

方法定义的形参类型为父类类型,实参类型允许为子类类型

应用实例

定义员工类 Employee,包含姓名和月工资 [ private ] ,以及计算年工资 getAnnual 的方法。

普通员工 和 经理 继承了 员工,经理类多了奖金 bonus 属性 和 管理 manage 方法,普通员工类多了work方法,普通员工 和 经理类 要求分别重写 getAnnual 方法

测试类 中添加一个方法 showEmpAnnal(Employee e),实现获取任何员工对象的年工资,并在main方法中调用该方法[e.getAnnual()]

测试类 中添加一个方法,testWork,如果是普通员工,则调用work方法,如果是经理,则调用 manage 方法

Employee 员工类

public class Employee {
	private String name;
	private double salary;

	public Employee(String name, double salary) {
		this.name = name;
		this.salary = salary;
	}

	//得到年工资的方法
	public double getAnnual() {
		return 12 * salary;
	}
	
	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public double getSalary() {
		return salary;
	}

	public void setSalary(double salary) {
		this.salary = salary;
	}
}

Worker 普通员工类

public class Worker extends Employee {
	public Worker(String name, double salary) {
		super(name, salary);
	}

	public void work() {
		System.out.println("普通员工 " + getName() + " is working");
	}

	//重写方法
	public double getAnnual() {	//因为普通员工没有其他收入,则直接调用父类方法
		return super.getAnnual();
	}
}

Manager 经理类

public class Manager extends Employee {
	private double bonus;

	public Manager(String name, double salary, double bonus) {
		super(name, salary);
		this.bonus = bonus;
	}

	public double getBonus() {
		return bonus;
	}

	public void setBonus(double bonus) {
		this.bonus = bonus;
	}	

	public void manage() {
		System.out.println("经理 " + getName() + " is managing");
	}

	//重写获取年薪的方法
	public double getAnnual() {
		return super.getAnnual() + bonus;
	}
}

PolyParameter 测试类

public class PolyParameter {
	public static void main(String[] args) {
		Worker tom = new Worker("tom", 2500);
		Manager milan = new Manager("milan", 5000, 200000);
		PolyParameter polyParameter = new PolyParameter();
		polyParameter.showEmpAnnual(tom);
		polyParameter.showEmpAnnual(milan);	

		polyParameter.testWork(tom);
		polyParameter.testWork(milan);
	}

	//测试类 中添加一个方法 showEmpAnnal(Employee e)
	//实现获取任何员工对象的年工资,并在main方法中调用该方法[e.getAnnual()]
	public void showEmpAnnual(Employee e) {
		System.out.println(e.getAnnual());	//动态绑定机制
	}

	//测试类 中添加一个方法,testWork,如果是普通员工,则调用work方法,如果是经理,则调用 manage 方法
	public void testWork(Employee e) {
		if (e instanceof Worker) {
			((Worker) e).work();	//向下转型
		} else if (e instanceof Manager) {
			((Manager) e).manage();	//向下转型
		} else {
			System.out.println("不做操作...");
		}
	}
}

7. Object 类详解

7.1 equals 方法

== 运算符

  1. ==:既可以判断基本类型,又可以判断引用类型
  2. ==:如果判断基本类型,判断的是值是否相等
  3. ==:如果判断引用类型,判断的是地址是否相等,即判定是不是同一个对象
public class Equals01 {
        public static void main(String[] args) {
                A a = new A();
                A b = a;
                A c = b;
                System.out.println(a == c);     //true
                System.out.println(b == c);     //true

                B bObj = a;
                System.out.println(bObj == c);  //true
        }
}

class B {}

class A extends B {}

equals 方法

  1. equals:是Object类中的方法,只能判断引用类型

  2. 默认判断的是地址是否相等,子类中往往重写该方法,用于判断内容是否相等。

比如 lnteger,String

public class Equals02 {
        public static void main(String[] args) {
                Integer integer1 = new Integer(1000);
                Integer integer2 = new Integer(1000);
                System.out.println(integer1 == integer2);       //false,地址不一样
                System.out.println(integer1.equals(integer2));  //true,内容相同

                String str1 = new String("hello");
                String str2 = new String("hello");
                System.out.println(str1 == str2);       //false,地址>不一样,内容相同
                System.out.println(str1.equals(str2));  //true
        }
}

重写 equals 方法

应用实例:判断两个 Person 对象的内容是否相等,如果两个Person对象的各个属性值都一样,则返回true,反之false。

public class EqualsExercise01 {
	public static void main(String[] args) {
		Person person1 = new Person("jack", 10, '男');	
		Person person2 = new Person("jack", 10, '男');

		//未进行 equals 方法重写
		//System.out.println(person1.equals(person2));	//输出为假,因为子类没有进行重写 equals 方法
		//所以要重写 Object 的 equals 方法

		//重写 equals 方法后
		System.out.println(person1.equals(person2));	//输出为真
	}
}

class Person {
	private String name;
	private int age;
	private char 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;
	}
	
	public Person(String name, int age, char gender) {
		this.name = name;	
		this.age = age;
		this.gender = gender;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	public char getGender() {
		return gender;
	}

	public void setGender(char gender) {
		this.gender = gender;
	}
}

查看输出的答案

public class EqualsExercise02 {
	public static void main(String[] args) {
			Person p1 = new Person();
		p1.name = "hello";

		Person p2 = new Person();
		p2.name = "hello";

		System.out.println(p1 == p2);	//false
		System.out.println(p1.name.equals(p2.name));	//true
		System.out.println(p1.equals(p2));	//false
	
		String s1 = new String("world");	
		String s2 = new String("world");	
	
		System.out.println(s1.equals(s2));	//true
		System.out.println(s1 == s2);	//false
	}
}

class Person {	//Person 类
	public String name;
}

查看为真为假

public class EqualsExercise03 {
	public static void main(String[] args) {
		int it = 65;
		float f1 = 65.0f;
		System.out.println("65 和 65.0f 是否相等? " + (it == f1));	//T

		char ch1 ='A' ; 
		char ch2 = 12;
		System.out.println("65 和 ‘A’ 是否相等? " + (it == ch1));	//T
		System.out.println("12 和 ch2 是否相等? " + (12 == ch2));	//T

		String str1 = new String("hello");
		String str2 = new String("hello");
		System.out.println("str1 和 str2是否相等? " + (str1 == str2));	//F
		System.out.println("str1 是否 equals str2? " + (str1.equals(str2)));	//T
		//System.out.println("hello" == new java.sql.Date());	//编译器直接报错。类型不同
	}
}

7.2 hashCode 方法

小结

  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 {}

7.3 toString 方法

基本介绍

默认返回:全类名 + @ + 哈希值的十六进制,子类往往重写toString方法,用于返回对象的属性信息

public String toString() {
	return getClass().getName() + "@" +Integer.toHexString(hashCode()); 
}

重写toString方法,打印对象或拼接对象时,都会自动调用该对象的 toString 形式

当直接输出一个对象时,toString方法会被默认的调用

public class ToString {
	public static void main(String[] args) {
		/**
 		* 
 		* object的toString()源码
 		* (1)getClass().getName()类的全类名(包名+类名)
 		* (2)Integer.toHexString(hashCode())将对象的hashCode值转成16进制字符串
 		* public String toString() {
 		* 	return getClass().getName() + "@" +Integer.toHexString(hashCode()); 
 		* }
 		*
 		* */

		Monster monster = new Monster("小妖怪", "巡山的", 1000);
		System.out.println(monster.toString());

		System.out.println("== 重写toString方法,打印对象或拼接对象时,都会自动调用该对象的 toString 形式  ==");	
		System.out.println(monster);	//等价于 monster.toString()
	}
}

class Monster {
	private String name;
	private String job;
	private double sal;

	public Monster(String name, String job, double sal) {
		this.name = name;
		this.job = job;
		this.sal = sal;
	}

	//重写 toString 方法,输出对象的属性
	@Override
	public String toString() {	//重写后,一般把对象的属性输出,也可以定制 toString 方法
		return "Monster{" + 
			"name="+ name + '\n' + 
			", job=" + job + '\n' + 
			", sal=" + sal + 
			'}';
	}
}

7.4 finalize 方法

  1. 当对象被回收时,系统自动调用该对象的finalize方法。子类可以重写该方法,做一些释放资源的操作
  2. 什么时候被回收:当某个对象没有任何引用时,则 jvm 就认为这个对象是一个垃圾对象,就会使用垃圾回收机制来销毁该对象,在销毁该对象前,会先调用finalize方法。
  3. 垃圾回收机制的调用,是由系统来决定(即有自己的 GC 算法),也可以通过 System.gc() 主动触发垃圾回收机制
public class Finalize {
	public static void main(String[] args) {
		Car bnw = new Car("宝马");
		bnw = null;	
		//这是 car 对象就是一个垃圾,垃圾回收器就会回收(销毁)对象,
		//在销毁对象前,会调用该对象的 finalize 方法
		//可以在finalize中,写自己的业务逻辑代码(比如释放资源:数据库连接,或者打开文件..)
		//如果不重写 finalize,那么就会调用0bject类的 finalize,即默认处理
		//如果重写了finalize,就可以实现自己的逻辑
		//
		
		//代码运行并没有打印重写 finalize 的信息
		//垃圾回收器不是随时随地工作的
		//
		//垃圾回收机制的调用,是由系统来决定(即有自己的 GC 算法),也可以通过 System.gc() 主动触发垃圾回收机制
		System.gc();	//主动调用垃圾回收器

		System.out.println("程序退出...");
			
	}
}	

class Car {
	private String name;

	public Car(String name) {
		this.name = name;
	}

	//重写 finalize
	@Override
	protected void finalize() throws Throwable {
		System.out.println("销毁 汽车 ");
		System.out.println("释放了某些资源");
	}
}

GitHub代码
gitee代码

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值