- 一、虚方法调用
严格意义上来说,在Java语言中是没有虚方法的,但是这种现象会出现,而且没有一种专有名词来表达,所以使用C++语言中的术语来表达这种现象
正常的方法调用
Person p = new Person();
p.showName();
Student s = new Student();
s.showName();
虚拟方法调用(多态情况下)
Person p = new Student();
p.showName(); //实际调用的是Student类的方法
编译时类型和运行时类型
运行时类型识别(run-time type identification ,RTTI)
只有程序运行时才创建对象
- 二、对象的造型(cast)
所谓对象造型就是Java对象间的类型转换,即:引用类型数据的强制类型转换
Java的对象造型可以分为两种情况: 自动造型;强制造型
Person p = new Student();//向上追溯(自动造型) 抽象的范围广可以自然而然的包含具体的,不需要额外人为的增加条件
Student s = (Student)p;//强制类型转换(强制造型) 将一个抽象的向一个具体的变量上转换,就需要强制类型转换了
对象的造型只用在有继承关系或接口实现的对象之间(即多态)
什么是向上转型、向下转型
对象的强制类型转换也称为向下造型,是将父类类型的对象变量强制地转换为子类类型。
向下转型什么时候能成功: 恢复对象的“本来面目”
在造型前可以使用“instanceof”运算符测试一个对象的类型,可以避免造型异常
数组中是否可以存在不同对象?可以
Person p = new Person();
Student s = new Student();
Object[] os = new Object[2];
os[0] = p;
os[1] = s;
【运行时多态】
通过一个父类变量发出的方法调用,可能执行的是该方法在父类中的实现,也可能是在某个子类中的实现,这只能在运行时刻根据该变量指向的具体对象确定,这就是运行时多态也称为运行时识别。
【多态的意义】
Java的多态性,突出的优点是使程序具有良好的可扩展性。
当程序从通用的基础类派生出任意多的新类型(子类),或向基础类中增加更多的方法时,无需修改原有对基础类进行处理的相关程序。并且可以处理这些新的类型,为程序增加新的功能。
- 三、内部类
允许一个类的定义出现在另一个类中,将处于另一个类中的“寄生类”称为“内部类”(inner class),也称为“类属类”
这样的一组类在逻辑上是一个整体,内部类和外层封装它的类之间存在逻辑上的从属关系
内部类对其封装类(即外部类)的内部成员有访问权限
外部类不能直接访问内部类中定义的非static成员变量
内部类有静态内部类和实例内部类,静态内部类用的非常少基本不用
内部类可以随意使用外部类的方法,外部类要使用内部类的方法必须要创建内部类的对象,通过内部类对象调用
public class Outer1 {
private int size;
/* 定义一个内部类,名称为: Inner */
public class Inner {
public void doStuff() {
// 内部类可以访问外部类的私有属性
size++;
}
}
public void testTheInner() {
Inner i = new Inner();
i.doStuff();
}
}
实例化内部类的两种方法:
//---方法一:建议使用这种方式
Outer.Inner in = new Outer().new Inner();
//---方法二:
Outer o = new Outer();
Outer.Inner I = o.new Inner();
//如果内部类是static的,也可以用下面方法:
Outer.Inner in = new Outer.Inner();
注:实例化实例内部类(非静态内部类)的前提是必须先有外部类的实例对象。
补充说明:
当一个类(A类)中的程序代码要用到另外一个类(B类)的实例对象,而另外一个类(B类)中的程序代码又要访问第一个类(A类)中的成员,将另外一个类(B类)做成第一个类(A类)的内部类,程序代码将会非常容易编写。
【局部内部类】
类的名字只能在定义的范围内使用,除非使用有效的全名;
内部类也可以定义在方法的内部。方法中final类型的局部变量,都可以被局部内部类的方法访问,但是不能访问所在方法中定义的非final类型的局部变量,可以访问外部类的成员变量。(参见下方示例)
注:方法中的内部类不能用任何访问控制符修饰,方法中定义的内部类只能在该方法中使用。
【内部类分类】
内部类可分为:成员(全局)内部类和局部内部类两种:
- 成员内部类:还可细分为:实例内部类和静态内部类两种
- 实例内部类: 必须存在外部类的实例; 内部类(包括多重内部类)可以访问所有外部类成员; 外部类必须通过实例访问内部类; 实例内部类不能有静态的成员(成员变量和方法统成为成员); (内部类分类和变量的分类类似)
- 静态内部类:创建实例不用有外部类实例; 可以直接访问外部类的静态成员; 可以有静态的和非静态的成员; 可以直接用外部类.内部类.成员来访问静态内部类的静态成员; 注:静态内部类使用非常少。
- 局部内部类:只能在当前方法中使用; 不能包含静态成员; 不能用public、protected、private等修饰局部内部类; 可以访问外部类成员(包括成员变量和方法),所在方法的final类型的输入参数(方法的形参)、方法中定义的使用final关键字修饰的局部变量,成员变量(这里指的是内部类的成员变量); 注:不能访问所在方法中非final类型的形参和方法中非final修饰的局部变量
【内部类特性】
内部类可以声明为抽象类 ,因此可以被其它的内部类继承。也可以声明为final的。
和外层类不同,全局内部类可以声明为private或protected
全局内部类可以声明为static的,但此时就不能再使用外层封装类的非static的成员变量;
非static的内部类(实例内部类)中的成员不能声明为static的,只有在顶层类或static的内部类中才可声明static成员。
【匿名内部类】
没有名称的内部类; 没有构造方法,调用父类的构造函数; 只有一种初始化方式就是自由块;
A a = new A(){
String s="Hello";
String getS(){return s;}
};
(new A() 其中“A” 是父类名称, 也可以是接口)
如果一个类仅仅使用一次,那这个类就没有必要编译为class文件存储,此时就可以使用匿名内部类
内部类的用法:一种封装类型的有效手段; 能够直接访问外部类的成员;
- 四、修饰符的适用范围
|
class |
属性 |
方法 |
构建器 |
自由块 |
全局内部类 |
public |
Y |
Y |
Y |
Y |
|
Y |
protected |
|
Y |
Y |
Y |
|
Y |
(Default) |
Y |
Y |
Y |
Y |
Y |
Y |
private |
|
Y |
Y |
Y |
|
Y |
final |
Y |
Y |
Y |
|
|
Y |
abstract |
Y |
|
Y |
|
|
Y |
static |
|
Y |
Y |
|
Y |
Y |