一、继承
继承的优点
- 减少了代码的冗余,提高了代码的复用性
- 便于功能的扩展
- 为多态性的使用提供了前提
继承格式
class A extends B {}
A:子类、派生类、subclass
B:父类、超类、superclass
- 一旦子类A继承父类B以后,子类A中就获取了父类B中声明的结构:属性和方法。特别地,父类中声明为private的属性和方法,子类继承父类以后,仍然认为获取了父类中私有的结构,只是因为封装性的存在,使得子类不能直接调用父类的结构而已。
- 子类继承父类后,还可以声明自己特有的属性和方法,实现功能的扩展。
关于继承的规定
-
一个类可以被多个子类继承
-
一个类只能有一个父类
-
子父类是相对的概念
-
子类直接继承的父类称为直接父类,间接继承的的父类称为间接父类,子类继承父类以后,就获取了直接父类以及所有间接父类中声明的属性和方法
-
如果我们没有显式地声明一个类的父类的话,则此类继承于
java.lang.Object
类 -
所有的Java类(除了
java.lang.Object
)都直接或间接地继承自java.lang.Object
类,意味着所有的Java类具有java.lang.Object
类中的属性和方法。二、方法的重写
定义:在子类中可以根据需要对从父类中继承来的方法进行改造。在程序执行时,子类的方法将覆盖父类的方法。
要求:
-
子类重写的方法必须和父类被重写的方法具有相同的方法名称、参数列表。
-
子类重写的方法的返回值类型不能大于父类被重写的方法的返回值类型。
- 父类被重写的方法的返回值类型为
void
,则子类重写的方法的返回值类型只能是void
- 父类被重写的方法的返回值为A类型,则子类重写的方法的返回值只能是A或者A 的子类
- 父类被重写的方法的返回值为基本数据类型,则子类重写的方法的返回值只能是相同的基本数据类型
- 父类被重写的方法的返回值类型为
-
子类重写的方法使用的访问权限不能小于父类被重写的方法使用的访问权限。
- 注意:子类不能重写父类中声明为
private
的方法
- 注意:子类不能重写父类中声明为
-
子类重写的方法抛出的异常不能大于父类被重写的方法抛出的异常。
注意:
重写以后,当创建子类对象后,通过子类对象调用子父类中的同名同参数的方法时,实际执行的是子类重写父类的方法。
三、 super
关键字
super
可以理解为:父类的。可以用来调用属性、方法、构造器。
super
调用属性、方法
- 我们可以在子类的方法或构造器中,通过
super.属性
或super.方法
的方式,显式地调用父类中声明的属性或方法。但是,通常情况下,我们习惯省略super.
。 - 特殊情况一:当子类与父类中定义了同名的属性时,我们想要在子类中调用父类声明的属性时,则必须显式地使用
super.属性
的方式,表明调用的是父类中声明的属性。 - 特殊情况二:当子类重写了父类的方法以后,我们在子类的方法中调用父类被重写的方法时,必须显式地使用
super.方法
的方式,表明调用的是父类中被重写的方法。
super
调用构造器
- 我们可以在子类的构造器中显式地使用
super(形参列表)
的方式,调用父类中声明的指定的构造器。 super(形参列表)
的使用,必须声明在子类构造器的首行。- 我们在类的构造器中,针对于
this(形参列表)
或super(形参列表)
,只能二选一,不能同时出现。 - 在构造器的首行,没有显式地声明
this(形参列表)
或super(形参列表)
,则默认调用的是父类中空参的构造器。 - 在类的多个构造器中,至少有一个类的构造器使用了
super(形参列表)
调用父类的构造器。
四、子类对象实例化的全过程
- 从结果上来看(继承性):子类继承父类以后,就获取了父类中声明的属性或方法。创建子类的对象,在堆空间中,就会加载所有父类中声明的属性。
- 从过程上来看:当我们通过子类的构造器创建子类对象时,我们一定会直接或间接地调用其父类的构造器,直到调用了
java.lang.Object
类中空参的构造器为止,正因为加载过所有的父类的结构,所以才可以在内存中看到父类中的结构,子类对象才可以考虑调用。 - 注意:虽然创建子类对象时,调用了父类的构造器,但是自始至终就创建过一个对象,即为new的子类对象。
五、多态性
- 对象的多态性:父类的引用指向子类的对象
//Man类继承自Person类
Person P1 = new Man();
- 虚拟方法调用(Virtual Method Invocation)
//正常的方法调用
Person e = new Person;
e.getinfo();
Student e = new Student();
e.getinfo();
//虚拟方法调用(多态情况下):
Person e = new Student;
e.getinfo();//调用Student类中的getinfo()方法
//子类中定义了与父类同名同参数的方法,在多态情况下,此时将父类的方法称为虚拟方法
//父类根据赋给它的不同子类的对象,动态调用属于子类的该方法,这样的方法调用在编译期是无法确定的
//编译时e为Person类型,而方法的调用是在运行时确定的,调用的是Student类中的getinfo()方法(动态绑定)
//所以说,多态是运行时行为
-
多态的使用:虚拟方法调用,即当调用子父类同名同参数的方法时,实际执行的是子类重写父类的方法。有了对象的多态性以后,我们在编译期,只能调用父类中声明的方法,但在运行期,我们实际执行的是子类重写父类的方法。
总结:编译看左边,运行看右边。 -
多态性的使用前提:类的继承关系、方法的重写。
//多态性的使用举例
class Animal{
public void eat(){
System.out.println("动物:进食");
}
public void shout(){
System.out.println("动物:叫");
}
}
class Dog extends Animal{
public void eat(){
System.out.println("狗吃骨头");
}
public void shout(){
System.out.println("汪!汪!汪!");
}
}
class Cat extends Animal {
public void eat() {
System.out.println("猫吃鱼");
}
public void shout() {
System.out.println("喵!喵!喵!");
}
}
public class AnimalTest{
public void func(Animal animal){
animal.eat();
animal.shout();
}
public static void main(String[] args) {
AnimalTest test = new AnimalTest();
test.func(new Dog());//体现多态性
test.func(new Cat());
}
}
-
对象的多态性,只适用于方法,不适用于属性。
-
方法重载与重写的区别:
- 二者的定义细节:(略)
- 从编译和运行的角度看:重载,是指允许存在多个同名方法,而这些方法的参数不同,编译器根据方法不同的参数列表,对同名方法的名称加以修饰。因此,对于编译器而言,这些同名方法就成了不同的方法,它们的调用地址在编译期就绑定了。Java的重载是可以包括父类和子类的,即子类可以重载父类的同名不同参数的方法。所以对于重载而言,在方法调用之前,编译器就已经确定了所要调用的方法,这称为“早绑定”或“静态绑定”。而对于多态,只有等到方法调用的那一刻,编译器才会确定所要调用的具体方法,这称为“晚绑定”或“动态绑定”。
-
向下转型的使用
X instanceof A;//检验X是否为类A的对象,返回值为boolean
//要求X所属的类与类A必须是子类与父类的关系,否则编译错误
//如果X属于类A的子类B,X instanceof A值为true
//使用情景:为了在向下转型时出现classCastException的异常,我们在向下转型之前先进行instanceof判断
//一旦返回true,就进行向下转型,如果返回false,不能进行向下转型
Person P1 = new Student();//P1不能调用子类所特有的方法
有了对象的多态性以后,内存中实际上是加载了子类特有的属性和方法的,但是由于变量声明为父类类型,导致编译时,只能调用父类中声明的属性和方法,子类特有的属性和方法不能调用
Student P2 = (Student)P1;//这样就可以调用子类特有的属性和方法
向下转型:使用instanceof
进行判断
向上转型:多态
六、Object类的使用
-
Object类是所有Java类的根父类,如果在类的声明中未使用
extends
关键字指明其父类,则默认父类为java.lang.Object
-
Object类的功能具有通用性
-
Object类只声明 了一个空参的构造器
-
==
与equals()
的区别:==
:运算符,可以使用在基本数据类型变量和引用数据类型变量中。如果比较的是基本数据类型,比较两个变量保存的数据是否相等(不一定类型要相同);如果比较的是引用数据类型,比较的是两个对象的地址值是否相同,即两个引用是否指向同一个对象实体。equals()
:是一个方法,只适用于引用数据类型。像String
,Data
,File
,包装类等都重写了Object
类中的equals()
方法,重写以后比较的不是两个引用的地址值是否相同,而是比较两个对象的“实体内容”是否相同。
//Object类中equals()的定义
public boolean equals(){
return (this==obj);//Object类中定义的equals()方法与==的作用是相同的
}
toString()
方法- 当我们输出一个对象的引用时,实际上就是调用当前对象的
toString()
方法。 Object
类中的toString()
方法会输出地址值。- 像
String
,Data
,File
,包装类等都重写了Object
类中的toString()
方法,使得在调用对象的toString()
时返回的是“实体内容”。
- 当我们输出一个对象的引用时,实际上就是调用当前对象的
七、包装类的使用
Java中的单元测试
- 选中当前工程—右键选择buildpath—add libraries—JUnit4—下一步
- 创建Java类,进行单元测试,此时的Java类的要求:①此类是public;②此类提供公共的无参的构造器
- 此类中声明单元测试方法,此时的单元测试方法:方法的权限是public,没有返回值,没有形参。
- 此单元测试方法上需要先声明注解:
@Test
,并在单元测试类中导入:import org.junit.Test
- 声明好单元测试方法以后,就可以在方法体内测试相关的代码。
- 写完代码以后,左键双击单元测试方法名,右键run as —JUnitTest。如果执行结果没有出现任何异常:绿条,反之为红条。
基本类型、包装类与String类间的转换
Java提供了8种基本数据类型对应的包装类,使得基本数据类型变量具有类的特征
- 基本数据类型—包装类:调用包装类的构造器
public void test1(){
int num1 = 10;
Integer in1 = new Integer(num1);
System.out.println(in1.toString());
Integer in2 = new Integer("123");
System.out.println(in2.toString());
}
- 包装类—基本数据类型:调用包装类的xxxValue()
public void test2(){
Integer in1 = new Integer(12);
int i1 = in1.intValue();
}
- JDK5.0新特性:自动装箱与自动拆箱
//自动装箱:替代上述基本数据类型—包装类
int num2 = 10;
Integer in1 = num2;
//自动拆箱:替代上述包装类—基本数据类型
int num3 = in1;
- 基本数据类型、包装类—String类型:有自动拆、装,视为一类。