java入门 -- 面向对象之多态

1 多态

▪ 多态是同一个行为具有多个不同表现形式或形态的能力。
▪ 父类的引用指向子类的对象(可以应用在抽象类和接口上)。
▪ 成员变量不具备多态性。

1.1 实现多态的前提

▪ 需要存在继承或者实现关系
▪ 有方法的重写

1.2 多态的形式

Animal a =new Cat();

父类的引用指向子类的对象,其中Cat是Animal的子类。

1.3 java引用变量的类型

编译时类型
编译时类型由声明该变量时使用的类型决定
编译时看的是父类的引用(父类中不具备子类特有的方法)
运行时类型
运行时类型由实际赋给该变量的对象决定
运行时看的是子类的对象(实际运行的是子类重写父类的方法)

对象的引用
一个引用类型变量可能指向(引用)多种不同类型的对象(因为有继承和多态)

Animal a =new Cat();
Object o = new Animal();//Object类型的变量o,指向Animal类型的对象
o = new Cat(); //Object类型的变量o,指向Cat类型的对象

实例

public class Animal {
      int szie;
     protected boolean Herbivorous;
     private boolean Carnivorous;
      String name;
	public Animal() {
		super();
	}
	public Animal(int szie, boolean herbivorous, boolean carnivorous, String name) {
		super();
		this.szie = szie;
		Herbivorous = herbivorous;
		Carnivorous = carnivorous;
		this.name = name;
	}
	public int getSzie() {
		return szie;
	}
	public void setSzie(int szie) {
		this.szie = szie;
	}
	public boolean isHerbivorous() {
		return Herbivorous;
	}
	public void setHerbivorous(boolean herbivorous) {
		Herbivorous = herbivorous;
	}
	public boolean isCarnivorous() {
		return Carnivorous;
	}
	public void setCarnivorous(boolean carnivorous) {
		Carnivorous = carnivorous;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	
	protected  void Say() {
		System.out.println("我是animal的say");
	}
}
public class Cat  extends Animal{
	  String varieties;
	  String sex;
	  int age;
	  
	@Override
	public String toString() {
		return "Cat [varieties=" + varieties + ", sex=" + sex + ", age=" + age + ", szie=" + szie + ", Herbivorous="
				+ Herbivorous + ", name=" + name + "]";
	}
	
	public Cat(int szie, boolean herbivorous, boolean carnivorous, String name, String varieties, String sex, int age) {
		super(szie, herbivorous, carnivorous, name);
		this.varieties = varieties;
		this.sex = sex;
		this.age = age;
	}

	public Cat(String varieties, String sex, int age) {
		super();
		this.varieties = varieties;
		this.sex = sex;
		this.age = age;
	}
	public Cat() {
		super();
	}
	public String getVarieties() {
		return varieties;
	}
	public void setVarieties(String varieties) {
		this.varieties = varieties;
	}
	public String getSex() {
		return sex;
	}
	public void setSex(String sex) {
		this.sex = sex;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	
	public void setHerbivorous(boolean herbivorous) {
		Herbivorous = herbivorous;
	}
	 @Override
	 public  void Say() {
//		    super.Say();
			System.out.println("我是Cat的say");
		}
}
public class Dog extends Animal{
	  String varieties;
	  String sex;
	  public Dog(int szie, boolean herbivorous, boolean carnivorous, String name, String varieties, String sex, int age) {
		super(szie, herbivorous, carnivorous, name);
		this.varieties = varieties;
		this.sex = sex;
		this.age = age;
	}
	int age;
	public Dog(String varieties, String sex, int age) {
		super();
		this.varieties = varieties;
		this.sex = sex;
		this.age = age;
	}
	public Dog() {
		super();
	}
	public String getVarieties() {
		return varieties;
	}
	public void setVarieties(String varieties) {
		this.varieties = varieties;
	}
	public String getSex() {
		return sex;
	}
	public void setSex(String sex) {
		this.sex = sex;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	@Override
	protected void Say() {
		// TODO Auto-generated method stub
		super.Say();
	}  
}
public class Test {
	public static void main(String[] args) {
		Cat c =new Cat();
		Dog d =new Dog();
		Animal a =new Cat();
		a.Say();
		Animal a1 =new Animal();
		a1.Say();
		c.Say();
		}
}

运行结果
在这里插入图片描述

可以看到c.Say(); 和a.Say();调用出了同一个Say方法。用一个父类型去引用子类对象时,会先访问到子类中重写的父类方法(父类的方法不会再执行),如果子类没有重写父类的方法,才会执行父类中的重写办法。同时,子类中没有继承到父类的部分,是不能被执行的。当我们把子类的注释掉后的结果为:
在这里插入图片描述
可以看到就调用了父类中的Say方法。
这种调用方式,就是多态的一种状态,叫做向上转型
注意在此方法下这样的方法调用在编译期是无法确定的。编译时a为Animal类型,而方法的调用是在运行时确定的,所以调用的是Cat的Say方法。

1.4 instanceof

x instanceof A:检验x是否为类A的对象,返回值为boolean型。

▪ 要求x所属的类与类A必须是子类和父类的关系,否则编译错误。
▪ 如果x属于类A的子类B,x instanceof A值也为true。

public class Test {
	public static void main(String[] args) {
		Cat c =new Cat();
		Dog d =new Dog();
		Animal a =new Cat();
		a.Say();
		Animal a1 =new Animal();
		a1.Say();
		c.Say();
	System.out.println(d instanceof Animal);
	System.out.println(c instanceof Animal);
//	System.out.println(c instanceof Dog); 这个编译错误
	}
}

在这里插入图片描述
有了对象的多态性以后,内存中实际上是加载了子类特有的属性和方法的,但是由于变量声明为父类类型,导致编译时,只能调用父类中声明的属性和方法。子类特有的属性和方法不能调用。
上面提到了向上转型,我们可以利用对象类型转换实现向下转型。就可以调用其特有的方法。
我们在Cat中添加一个方法

public void Unique() {
		 System.out.println("Cat中独特的方法");
	 }
public static void main(String[] args) {
		Cat c =new Cat();
		Dog d =new Dog();
		Animal a =new Cat();
		a.Say();
		Animal a1 =new Animal();
		a1.Say();
		c.Say();
		 Cat c1 = (Cat)a ;
		Dog d2 =(Dog)a;
		c1.Unique();
		}
		}

在这里插入图片描述

此时通过Cat c1 = (Cat)a ;调用出Cat中的其他方法,注意强转时可能出现ClassCastException的异常。Dog d2 =(Dog)a;这里的a不能用Dog进行强转。
ClassCastException是JVM在检测到两个类型间转换不兼容时引发的运行时异常。此类错误通常会终止用户请求。在执行任何子系统的应用程序代码时都有可能发生ClassCastException异常。

2 Object类

Object类是所有Java类的根父类
在类的中没有声名他的父类则则默认父类 为java.lang.Object类

2.1 ==和equals

▪ equals是判断两个变量或者实例指向同一个内存空间的值是不是相同
▪ ==是判断两个变量或者实例是不是指向同一个内存空间

▪ 用“==”进行比较时,符号两边的数据类型必须兼容(可自动转换的基本
数据类型除外),否则编译出错

▪ equals只能比较引用类型,其作用与“==”相同,比较是否指向同一个对象。
▪ 格式:obj1.equals(obj2)

Customer cust1 = new Customer("Tom",21);
Customer cust2 = new Customer("Tom",21);
System.out.println(cust1 == cust2);//输出false
System.out.println(cust1.equals(cust2));//输出true

这是因为equals比较的是其内容
Java中equals的源码:

public boolean equals(Object anObject) {
        if (this == anObject) {
            return 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;
            }
        }
        return false;
    }
    
 public boolean equals(Object obj) {
        return obj instanceof Date && getTime() == ((Date) obj).getTime();
    }

可以看到其本质上也是比较两个对象的每个属性是否都相同。
当自定义使用equals()时,可以重写。
重写equals的原则

▪ 对称性:如果x.equals(y)返回是“true”,那么y.equals(x)也应该返回是“true”。
▪ 自反性:x.equals(x)必须返回是“true”。
▪ 传递性:如果x.equals(y)返回是“true”,而且y.equals(z)返回是“true”,那么z.equals(x)也应该返回是“true”。
▪ 一致性:如果x.equals(y)返回是“true”,只要x和y内容一直不变,不管你重复x.equals(y)多少次,返回都是“true”。
▪ 任何情况下,x.equals(null),永远返回是“false”; x.equals(和x不同类型的对象)永远返回是“false”。

面试提问==和equals:

▪ 1 == 既可以比较基本类型也可以比较引用类型。对于基本类型就是比较值,对于引用类型就是比较内存地址
▪ 2 equals的话,它是属于java.lang.Object类里面的方法,如果该方法没有被重写过默认也是==;我们可以看到String等类的equals方法是被重写过的,而且String类在日常开发中用的比较多,久而久之,形成了equals是比较值的错误观点。
▪ 3 具体要看自定义类里有没有重写Object的equals方法来判断。
▪ 4 通常情况下,重写equals方法,会比较类中的相应属性是否都相等。

2.2 toString

在进行String与其它类型数据的连接操作时,自动调用toString()方法

System.out.println(a); 相当于
System.out.println(a.toString());


package tesk13;

public class test14 {
//tostring
   static String b ="helloworld";
   public static void main(String[] args) {
   	Csdn cs=new Csdn();
   	String a ="hello";
   	String c =new String("hellocsdn");		
   	System.out.println(a);
   	System.out.println(b);
   	System.out.println(c);
    System.out.println(cs);
   }

}
class Csdn extends CsdnTest {
   String d ="hellotest";

//	@Override
//	public String toString() {
//		return "Csdn [d=" + d + "]";
//	}
   
}
class CsdnTest{
   
}

在这里插入图片描述
当自定义类没有重写toString时输出的是tesk13.Csdn@15db9742
其原因是因为toString源码

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

包名.类名+@+哈希值
重写之后输出的是Csdn [d=hellotest]
在这里插入图片描述
建议所有自定义都重写toString方法。
在eclipse中点开Source>>Generate toString()即可重写。

3 包装类

针对八种基本数据类型定义相应的引用类型—包装类(封装类),有了类的特点,就可以调用类中的方法。
其用法与正常类用法无差别。
在这里插入图片描述
自动拆箱与自动装箱
基本数据类型的自动装箱(autoboxing)、拆箱(unboxing)是自jdk1.5开始提供的功能。
装箱就是自动将基本数据类型转换为包装器类型;拆箱就是自动将包装器类型转换为基本数据类型。
自动装箱:

例如:Integer i = 100;
相当于编译器自动为您作以下的语法编译:Integer i = Integer.valueOf(100);
这是它的源码

public static Integer valueOf(int i) {
       if (i >= IntegerCache.low && i <= IntegerCache.high)
           return IntegerCache.cache[i + (-IntegerCache.low)];
       return new Integer(i);
   }
   //其中low为-128,high最高位127

自动拆箱
Boolean b=new Boolean(true) ;
调用了

public boolean booleanValue() {
       return value;
   }

基本类型和String之间的转换
字符串转为基本数据类型
int i = new Integer(“12”);
调用了

public Integer(String s) throws NumberFormatException {
       this.value = parseInt(s, 10);
   }

基本数据类型转为字符串
String fstr = String.valueOf(2.34f);
调用了

public static String valueOf(float f) {
     return Float.toString(f);
 }

在这里插入图片描述
对于Integer

//Integer内部定义了IntegerCache结构,IntegerCache中定义了Integer[],
//保存了从-128~127范围的整数。如果我们使用自动装箱的方式,给Integer赋值的范围在
//-128~127范围内时,可以直接使用数组中的元素,不用再去new了。目的:提高效率
  	
  	Integer m = 1;
  	Integer n = 1;
  	System.out.println(m == n);//true

  	Integer x = 128;//相当于new了一个Integer对象
  	Integer y = 128;//相当于new了一个Integer对象
  	System.out.println(x == y);//false
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值