黑马毕向东Java课程笔记(day08):面向对象(第四部分)多态

  这一部分我们主要讲面向对象多态以及Object类。

1、多态
  多态:可以理解为事物存在的多种体现形态。比如,人分为男人和女人,对于一个男人,你既可以叫他男人,也可以叫他人;对于一只猫,你既可以叫他猫,也可以叫他动物,这就是一个事物存在多种体现形态。
  在java里面,我们可以以:猫 x = new 猫();这种形式来创建猫的对象,也可以以 动物 x = new 猫(); 这种形式来创建猫的对象。我们在java里面强调的是对象的多态性(函数也存在多态性,比如重载和重写)。
  总而言之,在java中,一个对象变量可以指示多种数据类型(指示多个类类型)的现象称为多态

1.1、多态的特点
  多态的体现形式:父类的引用指向子类的对象。
  我们用例子1来演示一下多态的体现

//这一段参考视频8.2部分的讲解
public class PolymorphicDemo {

	public static void main(String[] args) {
		//普通的调用猫的吃东西和狗的吃东西的做法,运行结果就是吃鱼  吃骨头
		Cat c = new Cat();
		c.eat();
		Dog d = new Dog();
		d.eat();
		
		//在这种情况下,如果我们想要另一只猫来实现吃东西的功能,就要创建一个猫的对象,这样很麻烦
		Cat c1 = new Cat();
		c1.eat();
		
		//想要实现不同猫对象的吃的功能,直接给function()方法赋值不同的对象即可
		PolymorphicDemo.function(new Cat());//提高了代码的复用性
		
		//但是这个时候又有一种情况,就是狗也有很多条,也都想要吃东西,我们还得建立一个狗的吃东西函数
		function(new Dog());
		
		//如果我们现在新加一个猪类的方法,对于很多只不同的猪,想要实现他们吃的方法,我们还要重载一个猪类对象的function方法,很麻烦
		function(new Pig());
		
		//如果我们再加入动物,这些动物都继承Animal类,有吃饭的功能,又各自有自己私有的功能
		//想要实现他们不同对象吃饭的功能,就要重载很多个function,很麻烦,那么我们写成如下的形式
		Animal obj = new Cat();//一个猫对象具备猫、动物等多种形态(多态特性)造成的
		obj.eat();//一样可以
		
		//上面说明父类的引用也可以接收子类的对象那么,对于动物所有的共性方法,我们用一个function重写出来
		//这个function的形参是Animal类的对象,我们想要调用某一个子类的某一个对象的eat()方法(也就是animal的共性方法)的时候
		//只需要在调用function函数的时候创建该子类的对象即可
		function(new Cat());//想要调用一个Cat对象中的eat()方法,只需要传入一个Cat的对象即可,这样我们就不需要重写多个function方法
		
		//上面这种方法,以后不管加入多少类型的动物,我们都不需要重写新的function函数,提高了代码的扩展性
		
	}
	
	//由于对于不同的猫,想要执行猫类的一个方法我们就要创建不同的对象
	//我们创建一个方法,参数为猫的对象(匿名对象),里面实现吃的方法,这样只要输入我们想要实现的猫的对象,就可以实现相应的猫吃的方法
//	public static void function(Cat c)
//	{
//		c.eat();
//	}
//	public static void function(Dog d)
//	{
//		d.eat();
//	}
//	public static void function(Pig p)
//	{
//		p.eat();
//	}
	//这个function的形参是Animal类的对象
	public static void function(Animal obj)
	{
		obj.eat();
	}
}

//--------------------------------------------
abstract class Animal
{
	public abstract void eat();
}

class Cat extends Animal
{
	public void eat()
	{
		System.out.println("吃鱼");
	}
	public void catchMouse()
	{
		System.out.println("抓老鼠");
	}
}
class Dog extends Animal
{
	public void eat()
	{
		System.out.println("吃骨头");
	}
	public void houseKeeping()
	{
		System.out.println("看家");
	}
}
//如果我们有很多种动物,比如还有一头猪
class Pig extends Animal
{
	public void eat()
	{
		System.out.println("吃饲料");
	}
	public void gongDi()
	{
		System.out.println("拱地");
	}
}

  那么根据上面的代码,我们总结出多态实现的几个条件:
1)类与类之间要有继承关系,或者类与接口之间要有实现关系;因为多态是通过以下2种方法实现的:
I、接口和实现接口并覆盖接口中同一个方法的几种不同的类体现的。
II、父类和继承父类并覆盖父类中同一方法几个不同子类实现的。(因为几个不同子类要实现同一个方法,我们用父类的引用来实现,这样便不需要创建那么多个子类对象);
2)子类要重写父类的方法,这一部分的原因是如果子类(多个)不重写父类的方法,那么我们将父类引用指向子类对像就毫无意义,因为没办法节省代码,更不要提代码的扩展性;
(这里也暴露了多态的弊端:虽然提高了扩展性,但是只能使用父类的引用访问父类中的成员,无法直接访问子类特有成员
3)父类引用指向子类对像;

  下面我们来说一下多态的定义格式:(存在三种情况的多态:普通类的多态,抽象类的多态,接口的多态)

/*多态的基本定义格式:
*    父类类型  变量名 = new 子类类型();
*    变量名.方法名();
*/
 
// 情况一:普通类多态定义的格式
// 父类 变量名 = new 子类();
public class Fu 
{ public void fun(){...}}	
public class Zi extends Fu 
{ public void fun(){ xxx } } // 子类重写父类方法	
Fu f = new Zi(); //类的多态使用
 
 
// 情况二:抽象类多态定义的格式
// 抽象类 变量名 = new 抽象类子类();
public class abstract class Fu 
{
	public abstract void method();
}
public class Zi extends Fu 
{
	public void method()
	{
		System.out.println(“重写父类抽象方法”);
	}
}
Fu fu= new Zi();  //类的多态使用
 
 
// 情况三:接口多态定义的格式
// 接口 变量名 = new 接口实现类();
interface Fu
{
	public abstract void method();
}
public class Zi implements Fu 
{
	public void method()
	{
		 System.out.println(“重写接口抽象方法”);
	}
}
Fu fu = new Zi();  //接口的多态使用

1.2、多态的类型转换

Employee p = new Programmer();	//向上转型

  分析:子类对象Programmer转化为父类对象Employee,向上转型时,这个时候Programmer这个引用 调用的方法是子类方法(具体成员如何访问看1.3)。子类单独定义的方法会丢失。比如上面Programmer类中定义的noholiday方法,当Employee类引用指向Programmer类实例时是访问不到noholiday方法的,Employee.noholiday会报错。
  子类引用不能指向父类对象。**Cat c = (Cat)new Animal()**这样是不行的。(强制类型转换把父类强制转换成子类是不行的)
  向上转型的好处:1.减少重复代码;2.使代码变得简洁;3.提高系统扩展性。

//向下转型 正确示范
Employee p = new Programmer();
Programmer t = (Prpgrammer)p;

//假设Hr类(招聘官)也是继承了Employee,和Programmer一样是Employee类的子类
//向下转型 错误示范
Employee p = new Programmer();
Hr t = (Hr)p;

//向下转型 错误示范
Employee p = new Employee();
Programmer t = (Prpgrammer)p;

//分析
第一个不报错是因为Programmer(程序员)本来就是Employee(员工),可以向下转型成员。这个时候p是父类Employee的引用,指向的是Programmer这个子类的对象,因此可以在创建新的子类引用t的时候,将p强制转换为子类Programmer的引用并赋予t,这样t就是子类对象指向子类引用,就可以调用子类特有的成员;

第二个报错是因为Hr和Programmer都是同一超类下的不同子类,程序员这种肥宅(逃)怎么可能转换成Hr。因为p是Programmer子类的对象,不是Hr子类的对象,因此无法强制转换为Hr子类的引用;

第三个报错是因为 p 为Employee对象,不是子类Programmer,因此不能进行向下转型
想要向下转型,就得先向上转型
这个时候t就能访问到属于自己的专属成员了	如t.noholiday();	t.myname

  最重要的,要明白,想要向下转型,就得先向上转型!!!
  只能在继承内层内进行类型转换;可以用instanceof运算符检测能否转换成功 if(Programmer instanceof Hr)
  例子2

//这一段参考视频8.2部分的讲解
public class PolymorphicDemo {
	public static void main(String[] args) {
		Animal obj = new Cat();
		obj.eat();//这样就调用了猫的eat()方法
		
		//如何调用猫特有方法,试试下面这种
		Cat c = new Cat();
		c.eat();//这种相当于多创建了一个猫的对象!!!
		
		//强行将父类的引用转换为子类引用!
		Cat obj1 = (Cat)obj;//这里是将对象obj赋予obj1,其实是同一个对象,并没有多增加对象
		obj1.catchMouse();//这样就可以调用猫类特有方法
		
		//千万不要出现这样的操作,就是将父类对象转成子类类型。
		//我们能转换的是父类应用指向了自己的子类对象时,该应用可以被提升,也可以被强制转换。
		//多态自始至终都是子类对象在做着变化。
//		Animal a = new Animal();
//		Cat c = (Cat)a;
		
		function(new Cat());
		//这样写就错误,编译通过,但是执行的时候会报错,狗不能转换为猫!
		function(new Dog());
	}
	
	public static void function(Animal obj)
	{
		obj.eat();
		//如果我们想在这个function方法里面调用猫的特有方法,就可以将形参向下类型转换
		//当然,这样在调用的时候就只能是function(new Cat());写其他类会报错,那么我们就判断一下
		if(obj instanceof Cat)
		{
			Cat c = (Cat)obj;
			c.catchMouse();
		}else if(obj instanceof Dog){
			Dog d = (Dog)obj;
			d.houseKeeping();
		}			
	}
}

//--------------------------------------------
abstract class Animal
{
	public abstract void eat();
}

class Cat extends Animal
{
	public void eat()
	{
		System.out.println("吃鱼");
	}
	public void catchMouse()
	{
		System.out.println("抓老鼠");
	}
}
class Dog extends Animal
{
	public void eat()
	{
		System.out.println("吃骨头");
	}
	public void houseKeeping()
	{
		System.out.println("看家");
	}
}

1.3、多态的成员访问过程
  前面提到了多态的特点,那么多态是如何具体实现,里面的成员又是如何具体访问的呢?
  先看看以下代码:

//-----------------
//测试类
class Test {
	public static void main(String[] args) {	
	Employee p = new Programmer();	//父类引用指向子类对象
	p.work();
	p.sleep();
	p.holiday();
	//以下俩个方法调用就是多态的缺点,无法直接访问子类特有成员
	//p.noholiday();
    //System.out.println(p.myname);
	System.out.println(p.name);
	System.out.println(p.age);
	}
}
//-----------------
//父类
class Employee {
	String name = "员工";
	static int age = 25;
	public void work() {
		System.out.println("员工的工作");
	}

	public static void sleep() {
		System.out.println("员工睡觉了");
	}

	public void holiday(){
		System.out.println("员工放假了");
	}
}
//-----------------
//子类
class Programmer extends Employee {	//子类继承了父类
	String name = "程序员";
	static int age = 90;
    String myname = "小明";
	public void work() {	//重写了方法
		System.out.println("小明在敲代码");
	}
	public static void sleep() {
		System.out.println("小明在加班");
	}
	public void noholiday() {	//子类特有属方法
		System.out.println("小明在公司度假");
	}
}

  代码的运行结果是

小明在敲代码
//父类的work()方法是普通方法,这里p调用work()方法属于多态,也就是动态调用,因此会先在子类中查询相应的work()方法,查找到,调用它;如果子类没有重写work()方法,则会动态调用父类的work()方法

员工睡觉了
//父类的sleep()是静态方法,静态方法和类相关,算不上重写,编译器确定要调用哪一个方法(静态方法在多态下直接调用父类的方法)(由于静态static不能与abstract同时出现,因此当方法是abstract时,遵从普通方法的调用模式),因此即使子类重写了sleep()方法,编译器还是会直接调用父类方法。

员工放假了
//子类没有重写holiday()方法,因此p直接调用父类的holiday()方法

员工
25
//对于成员变量,即使子类中修改了2个成员变量的值,但是p在调用成员变量的时候是看父类的,也就是说,在多态中,不管子类有没有修改父类的成员变量,子类的对象都会调用父类对成员变量的赋值;上面,我们如果不在父类中对name赋值,就会输出null。

总结出多态成员访问的特点:
1)成员变量:编译看左边(父类),运行看左边(父类);

2)成员方法:编译看左边(父类),运行看右边(子类),属于动态绑定;
在多态中非静态成员方法的特点:
在编译时期:参阅引用型变量所属的类(父类)中是否有调用的方法。如果有,编译通过,如果没有编译失败。(如果父类没有这个方法,我们用父类引用的对象去调用,肯定出错)
在运行时期:参阅对象所属的类(子类)中是否有调用的方法。如果子类没有重写父类方法,则调用父类的方法。如果子类重写父类的方法,就调用子类重写的方法。
简单总结就是:成员函数在多态调用时,编译看左边,运行看右边。

3)静态方法:编译看左边(父类),运行看左边(父类),属于静态绑定(静态方法被类所绑定)(静态和类相关,算不上重写,所以,访问还是左边的);因为在创建对象的时候父类会先加载进内存,那么他的静态方法也会加载进来,无论子类会不会重写父类的静态方法,父类的静态方法都不变,调用的时候都是调用父类的静态方法。
只有非静态的成员方法,编译看左边,运行看右边,这是我们多态用的最多的地方!

1.4、instanceof关键字
  instanceof关键字的作用:来判断某个对象是否属于某种数据类型。
  使用格式:boolean b = 对象 instanceof 数据类型;
  示例:
Person p1 = new Student(); // 前提条件,学生类已经继承了人类
boolean flag = p1 instanceof Student; //flag结果为true,p1指向了子类Student()的对象
boolean flag2 = p1 instanceof Teacher; //flag结果为false

1.5、多态示例
  例子1

public class PolymorphicDemo {

	public static void main(String[] args) {
		//想要调用2个子类的方法,普通做法
		BaseStudent bs = new BaseStudent();
		bs.study();
		bs.sleep();
		AdvStudent as = new AdvStudent();
		as.study();
		as.sleep();
		
		//下面这样就提高了代码的复用性
//		function(new BaseStudent());
//		function(new AdvStudent());
		
		//这种方法将function封装到另一个类中,使得代码更加简洁
		DoStudent ds = new DoStudent();
		ds.function(new BaseStudent());
		ds.function(new BaseStudent());
	}
	//这样如果我们日后加入其它类,还要做这些动作,而且虽然对象不同,但是方法是一样的,我们考虑将方法封装到另一个方法里面
	//我们还可以继续将这个方法封装到另一个类里面,想要统一调用这些学生,创建这个方法的对象即可
//	public void function(Student s)
//	{
//		s.study();
//		s.study();
//	}
}
//如果我们后期想加入其它学员类,直接加入即可,然后在主方法中用DoStudent的对象调用function方法,输入相应的对象即可
//以前是指挥每一个对象做事,现在是指挥一批对象做事
//--------------------------------------
//工具类,执行调用动作,我们在主函数中调用工具类的方法即可
class DoStudent
{
	public void function(Student s)
	{
		s.study();
		s.sleep();
	}
}
//--------------------------------------------
abstract class Student
{
	public abstract void study();
	public void sleep() {
		System.out.println("躺着睡");
	}
}

class BaseStudent extends Student
{
	public  void study()
	{
		System.out.println("base study");
	}
	//重写睡觉方法
	public void sleep() {
		System.out.println("坐着睡");
	}
}

class AdvStudent extends Student
{
	public  void study()
	{
		System.out.println("adv study");
	}
}

  例子2——(参考8-6视频):接口型引用指向子类对象


public class PolymorphicDemo {

	public static void main(String[] args) {
		/*
		MainBoard mb = new MainBoard();
		mb.run();
		//现在我们买了一张新的网卡,我们将其插入主板使用
		mb.useNetCard(new NetCard());
		*/
		//对于新的方法,我们同样创建主板的对象,先让主板运行
		MainBoard mb = new MainBoard();
		mb.run();
		//接下来,如果想插入一张某类型的网卡或声卡
		mb.useCard(new NetCard());
		mb.useCard(new SoundCard());
		//如果不插入任何卡
		mb.useCard(null);
		//对于这种方法,我们后面添加其他设备,就直接买设备(创建该设备的类),
		//插入PCI(在main方法里面使用主板的useCard方法,传入该设备的对象)即可,而不需要修改主板
	}
}
//------------------------------------------------
/*
//主板类,主板里面可以放网卡等功能
class MainBoard
{
	public void run()
	{
		System.out.println("mainboard run");
	}
	//我们将网卡添加到主板里面,但是添加什么型号的主板我们是不知道的(不同型号对应不同对象)
	//那么我们创建一个方法,形参为网卡类的引用,在使用的时候创建不同的网卡对象即可
	public void useNetCard(NetCard nc)
	{
		nc.open();
		nc.close();
	}
	//上面的操作,也就是说我如果想上网,就需要买一张网卡(添加一个网卡类),并将其焊接到主板上(主板创建一个使用网卡的方法)
	//当我想听音乐的时候,不仅仅要买一个声卡(创建一个声卡类),还需要把声卡焊接在主板上(在主板类中创建使用声卡的方法),很麻烦!
	//这种情况下网卡和主板的耦合性太强,如果你想听音乐,还得把网卡拆下来,再焊上声卡
	
}
//网卡类
class NetCard 
{
	public void open()
	{
		System.out.println("netcard open");
	}
	public void close()
	{
		System.out.println("netcard close");
	}	
}
*/
//--------------------------------------------------------------

//由于上面的方案耦合性太强,我们给主板提供一个PCI接口,这样不管插入什么卡,主板都可以直接使用它
//用java描述就是,我们创建一个PCI的接口,里面放上所有外设类的共性方法,再用外设类来实现它,
//然后在主板类中创建useCard方法,将其形参设置为PCI的引用,使用的时候根据不同的外设输入不同的对象即可
//这样就可以调用外设在PCI接口的共同方法,也可以向下类型转换,使用不同外设的特有方法
interface PCI
{
	//外设的共有方法是开启和关闭
	public abstract void open();
	public abstract void close();
}
//网卡类实现PCI接口
class NetCard implements PCI
{
	public void open()
	{
		System.out.println("netcard open");
	}
	public void close()
	{
		System.out.println("netcard close");
	}	
}
//声卡类实现PCI接口
class SoundCard implements PCI
{
	public void open()
	{
		System.out.println("SoundCard open");
	}
	public void close()
	{
		System.out.println("SoundCard close");
	}
}
//主板类,用于添加各种外设
class MainBoard
{
	public void run()
	{
		System.out.println("mainboard run");
	}
	
	public void useCard(PCI obj)//接口型引用指向自己的子类对象。
	{
		if(obj != null)
		{
			obj.open();
			obj.close();
		}else {
			System.out.println("没有使用外设");
		}
	}
	
}

  例子3——(参考8-7视频)

/*
需求:数据库的操作。
数据是:用户信息。
1,连接数据库,常用的连接方式:JDBC、Hibernate
2,操作数据库。
	c create r read  u update  d delete
3,关闭数据库连接。
*/

public class PolymorphicDemo {

	public static void main(String[] args) {
		//UserInfoByJDBC ui = new UserInfoByJDBC();
//		UserInfoByHibernate ui = new UserInfoByHibernate();
		UserInfoDao ui = new UserInfoByHibernate();
		ui.add(user);
		ui.delete(user);
	}
}
//------------------------------------------------
//注意,下面使用的都是伪代码
interface UserInfoDao
{
	public void add(User user);

	public void delete(User user);
}

class UserInfoByJDBC implements UserInofDao
{

	public void add(User user)
	{
		1,JDBC连接数据库。;
		2,使用sql添加语句添加数据。;
		3,关闭连接。
	}
	public void delete(User user)
	{
		1,JDBC连接数据库。;
		2,使用sql添加语句删除数据。;
		3,关闭连接。
	}
}

class UserInfoByHibernate implements UserInfoDao
{
	public void add(User user)
	{
		1,Hibernate连接数据库。;
		2,使用sql添加语句添加数据。;
		3,关闭连接。
	}
	public void delete(User user)
	{
		1,Hibernate连接数据库。;
		2,使用sql添加语句删除数据。;
		3,关闭连接。
	}
}

2、Object类
  理论上Object类是所有类的父类,即所有类都直接或间接的继承java.lang.Object类。由于所有的类都继承在Object类,因此省略了extends Object关键字。Object中定义的方法是所有对象都具备的方法!
  该类中主要有以下方法: toString()、getClass()、equals()、clone()、finalize(), 其中toString(),getClass(),equals()是其中最重要的方法。
  Object类中的getClass()、notify()、notifyAll()、wait()等方法被定义为final类型,因此不能重写。
  下面我们说一下Object类中的几个方法。参考如下文章
添加链接描述

2.1、equals()方法
  java认为所有的对象都具备比较性,都能比较2个对象是否相同。
  equals(Object obj) ,这里使用了多态的特性!!!

public boolean equals(Object obj)
{
	return this == obj;
}

  Object中的equals方法是直接判断this和obj本身的值是否相等,即用来判断调用equals的对象和形参obj所引用的对象是否是同一对象。所谓同一对象就是指内存中同一块存储单元,如果this和obj指向的同一块存储单元,则返回true,如果this和obj指向的不是同一块内存,则返回false。
  注意:即便是内容完全相等的两块不同的内存对象,也返回false。如果希望不同内存但相同内容的两个对象equals时返回true,则我们需要重写父类的equal方法,String类已经重写了object中的equals方法(这样就是比较内容是否相等了)

public class ObjectDemo {

	public static void main(String[] args) {
		Demo d1 = new Demo();
		Demo d2 = new Demo();
		Demo d3 = d1;
		//false,因为2个不同的对象会在堆内存中开辟2个不同区域
		System.out.println(d1.equals(d2));
		//true,因为d1与d3是同一个对象,同一个内存地址!!!
		System.out.println(d1.equals(d3));
	}
}
class Demo{}

  我们可能有比较内存不同但内容不知道是否相同的2个对象。如下:


public class ObjectDemo {

	public static void main(String[] args) {
		Demo d1 = new Demo(3);
		Demo d2 = new Demo(4);
		//用于比较对象的内容是否相同
		System.out.println(d1.compare(d2));
	}
}

class Demo
{
	private int num;
	public Demo(int num) 
	{
		this.num = num;
	}
	//用compare方法比较Demo2个对象的num值是否相同
	public boolean compare(Demo d)
	{
		return this.num == d.num;
	}
}

  如果自定义类中也有比较相同的功能,没有必要重新定义,只要沿袭父类中的功能,建立自己特有比较内容即可,这就是覆盖(重写)。比如如果希望比较不同内存但相同内容的两个对象equals时返回true,则我们需要重写父类的equal方法,如下:


public class ObjectDemo {

	public static void main(String[] args) {
		Demo d1 = new Demo(3);
		Demo d2 = new Demo(4);
		//用于比较对象的内容是否相同
		System.out.println(d1.equals(d2));
		//对于另一个类的对象,如果我们不在重写的equals方法中比较,会出错
		Person p = new Person(3); 
		System.out.println(d1.equals(p));
		//结果:2个对象不属于同类 false
	}
}

class Demo
{
	private int num;
	public Demo(int num) 
	{
		this.num = num;
	}
	//我们重写equals方法来比较内容
	public boolean equals(Object obj)//Object obj = d2
	{
		//此处使用多态,obj是Object类的引用,不可以直接使用Demo的私有变量num
//		return this.num == obj.num;
		//我们使用向下类型转换就可以调用子类特有的内容
		if(obj instanceof Demo)
		{
			Demo d = (Demo)obj;
			return this.num == d.num;
		}else {//如果我们传入的对象不是Demo类型,内容肯定不同(就算数字相同,但是对象不同内容必然不同)
			System.out.println("2个对象不属于同类");
			return false;
		}
	}
}

class Person
{
	private int num;
	public Person(int num) 
	{
		this.num = num;
	}
}

2.2、toString()方法
  java认为所有对象都能转换为字符串被打印
  Object 类的 toString 方法返回一个字符串,该字符串由类名(对象是该类的一个实例)、at 标记符“@”和此对象哈希码的无符号十六进制表示组成。该方法用得比较多,一般子类都有覆盖。

public String tostring()
{ 
	return getClass(). getName()+"@"+Integer. toHexstring (hashCode());//其实getClass()与hashCode()方法前面都应该加一个this表示当前对象
}

  例子如下

public class ObjectDemo {
	public static void main(String[] args) {
		Demo d1 = new Demo();
		Demo d2 = new Demo();
		System.out.println(d1.toString());
	}
}
class Demo{}

2.3、hashCode()方法

public int hashCode()

  返回该对象的哈希码值

public class ObjectDemo {
	public static void main(String[] args) {
		Demo d1 = new Demo();
		System.out.println(d1.toString());
		System.out.println(Integer.toHexString(d1.hashCode()));
	}
}
class Demo{}
//结果
Demo@7852e922
7852e922

2.4、getClass()方法

public final Class getClass()//返回类名,返回值是Class类,比较特殊
//Class类用于描述class文件,就是***.class文件。

  返回次Object的运行时类类型。不可重写,要调用的话,一般和getName()联合使用,如getClass().getName()。(这一部分具体见8.9第6分钟开始的分析,目前比较难理解)
  这其中又涉及getName()方法,这个方法以 String 的形式返回此 Class 对象所表示的实体(类、接口、数组类、基本类型或 void)名称。

public class ObjectDemo {

	public static void main(String[] args) {
		Demo d1 = new Demo();
		//返回d1所对应的Demo类类型,将这个Demo类型赋予Class类的对象c;
		//因为Class类本来就是用来描述类文件“.class”文件的,此时c代表了“Demo.class”这个类,因此可以用Class类的getName()方法来获取Demo的类名
		Class c = d1.getClass();	
		System.out.println(c.getName());//结果是Demo
		System.out.println(d1.getName());//这样就会出错,因为Demo中没有getName()方法
	}
}
class Demo{}

  例子:

public class ObjectDemo {

	public static void main(String[] args) {
		Demo d1 = new Demo(4);
		System.out.println(d1.toString());
		//结果是:Demo:4
		//在实际开发过程中,java很多方法需要重写才能符合我们的使用需求
	}
}

class Demo
{
	private int num;
	Demo(int num)
	{
		this.num = num;
	}
	//我们发现toString类返回值没有什么意义,我们将其复写
	public String toString()
	{
		return "Demo:"+num;
	}
}

3、多态的补充(就业班)
  多态下成员变量与成员方法的访问特点:与普通区块下相同,变量看左边,方法看new谁

/*
在多态的代码当中,成员方法的访问规则是:
    看new的是谁,就优先用谁,没有则向上找。

口诀:编译看左边,运行看右边。

对比一下:
成员变量:编译看左边,运行还看左边。
成员方法:编译看左边,运行看右边。
 */
 /*
访问成员变量的两种方式:

1. 直接通过对象名称访问成员变量:看等号左边是谁,优先用谁,没有则向上找。
2. 间接通过成员方法访问成员变量:看该方法属于谁,优先用谁,没有则向上找。
(成员变量不能进行覆盖)
 */
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值