day13
review
- 多态性?
父类的引用指向子类的对象
- 虚拟方法调用?
当我们通过变量名去调用子父类中同名、同参方法时,编译时任务调用的是父类中的方法,实际执行时调用的是子类中重写的方法
- 方法重写(override)的具体规则
- 子类中重写的
方法名
、形参列表
与父类中的相同 - 子类中重写方法的
权限修饰符
不小于父类中的方法 - 子类中重写方法抛出的
异常
不大于父类方法中抛出的异常 - 子类中重写方法的
返回值
不大于父类方法的返回值,具体表现为:- 当父类方法返回值为
void
或基本数据类型
时,子类重写方法的返回值必须父类中的相同 - 当父类方法的返回值为
引用数据类型A
时,子类重谢方法的返回值为相同的引用数据类型A
或者A的子类
- 当父类方法返回值为
开发中重写时,经常直接复制父类方法声明或者用快捷键补全
- super
调用父类中指定的构造器
- 为什么
super()
和this()
调用语句只能作为构造器中的第一条语句出现?
无论通过哪个构造器创建子类对象,需要保证先初始化父类。
目的:当子类继承父类后,“继承"父类中所有的属性和方法,因此子类有必要知道父类如何为对象进行初始化
instanceof
- 向下转型
引入:有了对象的多态性以后,内存中实际上是加载了子类特有的属性和方法的,但是由于变量声明为父类类型导致编译时,只能调用父类中声明的属性和方法。子类特有的属性和方法不能调用。
- 如何调用子类中特有的属性和方法?
使用强制类型转换符()
强制转换时,可能出现
ClassCastException
异常,为了避免上述异常,所以引入instacnceof
- 使用:
a instanceof A
判断对象a
是否为类A
的实例,如果是,返回true
,否则返回false
-
使用情景:为了避免在向下转型时,出现
ClassCastException
异常,因此在向下转型之前先用instacnceof
进行判断,一旦返回true
,则进行向下转型,否则不进行 -
如果
a instanceof A == true
,则a instanceof B == true
, 其中A
是B
的子类 -
练习
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);
}
}
public class FeildMethodTest {
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
}
}
由上述练习:
- 若子类重写了父类方法,就意味着子类里定义的方法彻底覆盖了父类里的同名方法,系统将不可能把父类里的方法转移到子类中。
- 对于实例变量则不存在这样的现象,即使子类里定义了与父类完全相同的实例变量,这个实例变量依然不可能覆盖父类中定义的实例变量
上述两点体现为:方法的编译看左边,运行看右边;变量的编译运行都看左边
实战
public class InterviewTest {
public static void main(String[] args) {
Base base = new Sub();
base.add(1, 2, 3); // (1)
Sub s = (Sub)base;
s.add(1, 2, 3); // (2)
}
}
class Base {
public void add(int a, int... arr) {
System.out.println("base");
}
}
class Sub extends Base {
public void add(int a, int[] arr) {
System.out.println("sub_1");
}
public void add(int a, int b, int c) {
System.out.println("sub_2");
}
}
// (1)sub_1
// (2)sub_2
Object类
Object
是所有类的根父类- 如果在类的声明中未显式地使用
extends
关键字指明其父类,则默认父类为java.lang.Object
类 Object
类中的属性和方法具有通用性
- 属性:无
- 常用方法:
- equals()
- toString
- getClass()
- hashCode()
- …
Object
类中只定义了一个空参的构造器
equals()
- 回顾
==
的使用
==
是一个运算符,可以使用在基本数据类型变量和引用数据类型的变量中- 如果比较的是基本数据类型变量,则比较两个变量保存的数据是否相等(不一定类型相同)
- 如果比较的是引用数据类型变量,则比较两个对象的地址值是否相同,即比较
两个引用是否指向同一个对象实体
==
使用时,必须保证符号左右两边的变量类型“一致”
- equals()
equals()
是一个方法,而非运算符- 只适用于引用数据类型
Object
类中equals()
的定义:与==
作用相同,即比较两个对象的地址值是否相同
// Object源码中equals()的实现
public boolean equals(Object obj) {
return (this == obj);
}
String
、Date
、File
、包装类
都重写了Object
的equals()
方法。重写之后,比较的不再是两个引用的地址是否相同,而是比较两个对象**实体内容(属性)**是否相同
- 通常情况下,我们自定义的类如果使用
equals()
方法时,比较的也是类中实体内容是否相同,因此需要重写Object
类中的equals()
方法
重写规则:比较两个对象的实体内容是否相同
// String源码中equals()的实现:重写
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length; // String底层是char[]
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;
}
开发过程中,一般不会手撕
equals()
,而是使用IDE
自动生成
public class EqualsTest {
String name;
int age;
// IDE自动生成
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
EqualsTest other = (EqualsTest) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
}
==
和equals()
的区别
toString()
- 当我们输出一个对象的引用时,实际上就是调用当前对象的
toString()
方法 Object
中toString()
方法的定义
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
String
、Date
、File
、包装类
都重写了Object
的toString()
方法,重写之后,使得在调用toString()
方法时,返回对象的**实体内容(属性)**信息
// String中对于toString的重写
public String toString() {
return this;
}
- 自定义类也可以重写
toString()
方法以返回对象的实体信息,当然也可以通过IDE
生成
public class ToStringTest {
String name;
int age;
// IDE自动生成
@Override
public String toString() {
return "ToStringTest [name=" + name + ", age=" + age + "]";
}
}
单元测试
build path
→add libraries
→JUnit 4
- 创建Java类进行单元测试
- 类是
public
的, - 类提供公共的无参构造器
- 在此类中声明单元测试的方法
- 方法的权限是
public
- 返回值为
void
- 方法没有形参
- 单元测试方法之上需要声明注解:
@Test
,并在测试类中导入org.junit.Test
- 声明好单元测试方法之后,就可以在方法体内测试相关代码
- 写完代码之后,左键双击选中单元测试方法名,右键
run as -> JUnit Test
说明:
- 如果执行结果没有异常:绿条
- 如果执行结果出现异常:红条
包装类
- 八种基本数据类型都定义了相应的引用类型,称之为包装类。
为什么引入包装类?
为了使基本数据类型的变量具有类的特征
对应关系如下
- 基本数据类型、包装类、String三者之间的相互转换
- 基本数据类型–>包装类:自动装箱/调用包装类的构造器
- 包装类–>基本数据类型:自动拆箱/调用包装类
Xxx
的xxxValue()
方法 - 基本数据类型/包装类–>String类型 :
- 连接运算
- 调用String类中重载的
valueOf(Xxx xxx)
方法
- String类型–>基本数据类型/包装类:调用包装类中的
parseXxx()
方法
- 自动装箱与自动拆箱
JDK 5.0
新特性
- 自动装箱:基本数据类型->包装类
- 自动拆箱:包装类->基本数据类型
int m = 10;
Integer j = m; // 自动装箱
Integer i = new Integer(10);
int n = i; // 自动拆箱
- 练习
@Test
public void test1() {
Object o1 = true ? new Integer(1) : new Double(2.0);
System.out.println(o1); // (1)
}
@Test
public void test2() {
Object o2;
if (true) {
o2 = new Integer(1);
} else {
o2 = new Double(2.0);
}
System.out.println(o2); // (2)
}
@Test
public void test3() {
Integer i = new Integer(1);
Integer j = new Integer(1);
System.out.println(i == j); // (3)
Integer m = 1;
Integer n = 1;
System.out.println(m == n); // (4)
Integer x = 128;
Integer y = 128;
System.out.println(x == y); // (5)
}
// (1)1.0 编译时 int 提升为 double
// (2)1
// (3)false new 出来的对象地址不一样
// (4)true Integer 有一个static Integer cache[], 初始化存储-128~+127的的数字, 自动装箱时不用new
// (5)false 超过上述范围,需要new