Java面向对象的三大特征:封装,继承,多态。
一、封装
封装就是把类的数据和方法结合为一个整体,将数据在隐藏在类的内部,创建对象后不允许外部直接访问对象的数据,但是允许通过该类提供的方法来实现对这些数据的访问和操作,同时尽可能隐藏方法的实现细节。
二、继承
继承就是是子类从父类中继承数据和方法,并进行扩展。
子类实例化的时候,系统会为父类成员分配内存空间,也会为子类成员分配内存空间。当调用父类构造方法的时候,并不是创建了父类对象,而是为父类实例成员变量进行初始化。如下图:
1.继承的限制
父类构造方法不能被子类继承。
子类只能有一个直接父类(单继承),但是可以有多个间接父类(多层继承)。
子类继承父类所有的成员变量和方法,包括私有权限的成员变量和方法,但是子类不能直接访问从父类继承来的的私有权限的成员变量和方法(这就相当于不能继承)。
final修饰的类和private修饰构造方法的类都不能发生继承。
关于final变量和final方法的继承,子类是能够继承父类final变量和final方法的,其中final变量可以被子类隐藏,final不能被覆盖也不能被隐藏。
关于静态变量和静态方法的继承:子类是能够继承父类的静态变量和静态方法的,但是子类不能覆盖静态方法,只能隐藏静态方法。如下图:
public class RunTime {
public static void main(String[] args) {
Cat a = new Cat();
System.out.println(a.A);
a.voice();
}
}
class Animal {
public static int A = 0;
public void voice() {
System.out.println("动物叫");
}
}
class Cat extends Animal {
}
动物叫
2.隐藏
隐藏针对的是父类中的成员变量和静态方法而言。当子类的成员变量或者静态方法与父类的成员变量或者静态方法同名的时候,会发生隐藏,即子类的成员变量或者静态方法会屏蔽掉父类成员变量或者静态方法。代码如下:
public class RunTime {
public static void main(String[] args) {
Cat a = new Cat();
System.out.println(a.A);
System.out.println(a.b);
a.voice();
a.method();
}
}
class Animal {
public int b = 0;
public static int A = 0;
public static void method(){
System.out.println("动物");
}
public void voice() {
System.out.println("动物叫");
}
}
class Cat extends Animal {
public int b = 4;
public static int A = 4;
public static void method(){
System.out.println("猫");
}
public void voice() {
System.out.println("猫叫");
}
}
4
4
猫叫
猫
3.覆盖
覆盖是对于实例方法而言。
子类方法的返回值类型应比父类方法的返回值类型更小或者相等。
子类方法声明抛出的异常类应比父类方法声明抛出的异常类更小或相等。
子类方法的访问权限要比父类方法的访问权限更大或者相等。
4.super关键字
如果没有发生隐藏或者覆盖,子类可以访问从父类中继承的除了private修饰的以外实例变量和实例方法,还有静态变量、静态方法。
当发生隐藏和覆盖时,子类无法访问父类中被隐藏的实例变量、静态变量、静态方法或者被覆盖的实例方法。
super关键字可以让子类访问父类中被隐藏的实例变量和被覆盖的实例方法,而被隐藏的静态变量和静态方法可以通过父类名.静态变量或者静态方法的方式访问。
注意:
super不能出现在static修饰的方法中
子类构造方法总会调用父类构造方法一次,若没有super调用,系统将在执行子类构造方法前隐式调用父类无参构造方法,若有super调用,super调用父类构造器必须出现在子类构造器的执行体的第一行,此为显示调用。
5.继承与组合
组合是将一个类的对象当做另一个类的中用private修饰的成员变量来使用。如下图:
public class Test {
public static void main(String[] args) {
New newclass = new New();
newclass.newMethod();
}
}
class New {
private Old old = new Old();
public void newMethod() {
old.oldMethod();
}
}
class Old {
public void oldMethod(){
System.out.println("Old");
}
}Old
继承和组合都可以实现类的复用,继承与组合的内存开销差别不大。但是继承要表达的是一种is-a关系,而组合要表达是一种has-a关系。
三、多态
1.多态的概念
相同的引用变量,调用同一个方法的时候出现不同的行为特征,这就是多态。
2.多态的原理
Father f = new Son(),Father是引用变量f 的编译时类型,Son是引用变量f 的运行时类型。
多态实际上是一种动态绑定,引用变量在编写代码(也可以说是编译的时)的指向的运行时类型是不确定的,在运行期间才判断引用变量的运行时类型,根据它的运行时类型调用它实例化对象的方法,这个方法必须是发生了覆盖的。
多态发生的三个条件继承、覆盖、向上转型。
public class Test {
public static void main(String[] args) {
Father f = new Son();
f.test();
}
}
class Father {
public void test() {
System.out.println("Father");
}
}
class Son extends Father {
public void test(){
System.out.println("Son");
}
}
Son
引用变量f 在运行时指向它的运行时类型实例化对象new Son(),调用该类型中覆盖自父类的方法test()。
注意:
引用变量在编写代码(也可以说是编译时)只能调用编译时类型的方法,运行时执行它运行时类型所具有的方法。
引用变量访问成员变量和静态方法的时候访问的是它的编译时类型中的成员变量和静态方法,而不是运行时类型中的成员变量和静态方法。
3.instanceof运算符
向下转型,也就是引用变量的强制类型转换。
如果父类的引用指向的是子类对象,如Father f = new Son(),Son s = (Son)f,那么在向下转型过程中是正确的。如果父类的引用指向的是父类的对象,如Father f = new Father(),Son s = (Son)f,这种向下转型是错误的。
为了避免这种向下转型的错误,使用instanceof运算符。instanceof 前面的引用变量指向的对象是否是后面的类的实例,如:if(f instanceof Son) Son s = (Son)f 。
本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。