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