06 访问修饰符 封装 继承 多态
访问修饰符
- public 公开级别,对外公开
- protected 受保护级别,对子类和同一个包中的类公开
- default 默认级别,无修饰符,向同一个包的类公开
- private 私有级别,只有类本身可以访问,不对外公开
修饰符可以用来修饰类中的属性,成员方法以及类
只有默认和public才能修饰类
封装 encapsulation
好处:
1)隐藏实现细节
2)可以对数据进行验证,保证安全合理
封装的实现步骤
- 将属性进行私有化 private,使得外部不能直接修改属性
- 提供一个公共的(public)set方法,用于对属性判断并赋值
public void setXxx(类型 参数名){//加入数据验证的业务逻辑属性 = 参数名;}public void setXxx(类型 参数名) { //加入数据验证的业务逻辑 属性 = 参数名; }public void setXxx(类型 参数名) { //加入数据验证的业务逻辑 属性 = 参数名; }
- 提供一个公共的(public)get方法,用于获取属性的值
public XX getXxx(){//权限判断return Xx;}public XX getXxx() { //权限判断 return Xx; }public XX getXxx() { //权限判断 return Xx; }
继承 extends
好处:
代码复用,提高扩展性和维护性
细节:
-
子类继承了所有的属性和方法,但是私有/默认属性和方法不能在子类直接访问,要通过公共的方法去访问;
(很多情况子类和父类在同一个包内,默认权限也可以直接访问) -
子类必须调用父类的构造器,完成父类的初始化
(在创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器(默认执行super()),如果父类没有提供无参构造器,则必须在子类的构造器中用super去制定使用父类的哪个构造器完成对父类的初始化工作,否则编译不通过) -
如果希望指定去调用父类的某个构造器,则显式的调用:super(参数列表)
(super在使用时,需要放在构造器第一行) -
super() 和 this()都只能放在构造器第一行,因此这两个方法不能共存在一个构造器。(有this()时,系统不会默认执行super())
-
Java的所有类都是Object类的子类,Object是所有类的基类
-
父类构造器的调用不限于直接父类。将一致网上追溯直到Object类
-
子类最多只能(直接集成)一个父类,即单继承机制
继承的本质分析
例:
public class Test{public static void main(String[] args){Son son = new Son(); //分析内存的布局}}class GrandPa{String name = "大头爷爷";String hobby = "旅游";}class Father extends(GrandPa){String name = "大头爸爸";int age = 39;}class Son extends Father{String name = "大头儿子";}public class Test { public static void main(String[] args) { Son son = new Son(); //分析内存的布局 } } class GrandPa { String name = "大头爷爷"; String hobby = "旅游"; } class Father extends(GrandPa) { String name = "大头爸爸"; int age = 39; } class Son extends Father { String name = "大头儿子"; }public class Test { public static void main(String[] args) { Son son = new Son(); //分析内存的布局 } } class GrandPa { String name = "大头爷爷"; String hobby = "旅游"; } class Father extends(GrandPa) { String name = "大头爸爸"; int age = 39; } class Son extends Father { String name = "大头儿子"; }
-
首先在方法区加载父类信息:Object类、Grandpa类、Father类、Son类
-
然后在堆中分配地址空间给son:
* 首先给Grandpa类的属性(name和hobby)分配空间;
* 再继续给Father类的属性(name和age)分配空间 [注:重名属性name不会冲突,是独立空间] ;
* 最后还会给Son类的属性分配空间name -
最后把堆中的地址返回给栈中的对象名son
System.out.println(son.name); //大头儿子System.out.println(son.age); //39System.out.println(son.hobby); //旅游System.out.println(son.name); //大头儿子 System.out.println(son.age); //39 System.out.println(son.hobby); //旅游System.out.println(son.name); //大头儿子 System.out.println(son.age); //39 System.out.println(son.hobby); //旅游
按照查找关系来返回信息
首先看子类是否有该属性
如果子类有这个属性并且可以访问:则返回信息;
(若是private,内存中也会有这个属性,不过无法直接通过子类访问,则会报错)
如果子类没有这个属性:
则看父类有没有这个属性,
如果父类有该属性并且可以访问:就返回信息;
否则继续向上查找直到Object类
super关键字
- super代表父类的引用,用于访问父类的属性、方法、构造器(private属性和方法除外);
- 当子类中有和父类中的成员(属性和方法)重名时,为了访问父类的成员,必须通过super;如果没有重名,使用super、this、直接访问是一样的效果;
- super的访问不限于直接父类,如果爷爷类和本类中有同名的成员,也可以使用super去访问爷爷的成员;如果多个上级类中都有同名的成员,则遵循就近级原则(同上查找关系);
多态
方法重写/覆盖
- 方法重写/覆盖就是子类有一个方法,和父类的某个方法的名称、返回类型、参数一样,那么我们就说这个子类的这个方法覆盖了父类的那个方法
- 子类的方法的参数、方法名,要和父类方法的参数、方法名一样
- 子类方法的返回类型和父类方法返回类型一样,或者是父类返回类型的子类
- 子类方法不能缩小父类方法的访问权限
方法重写和重载的比较
多态:方法或对象具有多种形态。多态是建立在封装和继承之上的。
具体体现:
- 方法的多态:重写和重载就体现多态
- 对象的多态:
(1)一个对象的编译类型和运行类型可以不一致
(2)编译类型再定义对象时,就确定了,不能改变
(3)运行类型是可以变化的
(4)编译类型看定义时 =号 的左边,运行类型 =号 的右边
例:
//父类的引用可以指向子类的对象Animal animal = new Dog(); //animal编译类型是Animal,运行类型是Doganimal.cry(); // 输出的是Dog里重写过的cryanimal = new Cat(); // animal的运行类型变成了Cat,编译类型仍是Animalanimal.cry(); // 输出的是Cat里重写过的cryanimal.catchMouse();//error!这是Cat的特有方法,无法调用!//父类的引用可以指向子类的对象 Animal animal = new Dog(); //animal编译类型是Animal,运行类型是Dog animal.cry(); // 输出的是Dog里重写过的cry animal = new Cat(); // animal的运行类型变成了Cat,编译类型仍是Animal animal.cry(); // 输出的是Cat里重写过的cry animal.catchMouse();//error!这是Cat的特有方法,无法调用!//父类的引用可以指向子类的对象 Animal animal = new Dog(); //animal编译类型是Animal,运行类型是Dog animal.cry(); // 输出的是Dog里重写过的cry animal = new Cat(); // animal的运行类型变成了Cat,编译类型仍是Animal animal.cry(); // 输出的是Cat里重写过的cry animal.catchMouse();//error!这是Cat的特有方法,无法调用!
细节:
-
前提:两个对象(类)存在继承关系
-
多态的向上转型
- 本质:父类的引用指向了子类的对象
- 语法:父类类型 引用名 = new 子类类型()
- 特点:编译类型看左边,运行类型看右边
- 调用规则:
* 可以遵守访问权限调用父类中的所有属性和成员,但是不能调用子类的特有属性和方法,因为在编译阶段,能调用哪些成员是由编译类型来决定的。
* 最终运行效果看子类的具体实现,与前面查找关系规则一致。
-
多态的向下转型
- 语法:子类类型 引用名 = (子类类型)父类引用
Cat cat = (Cat) animal
(相当于有cat、animal两个引用指向这个子类对象了) - 只能强转父类的引用,不能强转父类的对象
- 求父类的引用必须指向的是当前目标类型的对象
- 向下转型后,可以调用子类类型中所有的成员
- 语法:子类类型 引用名 = (子类类型)父类引用
-
属性没有重写之说,属性的值看编译类型
例:
public class Test{public static void main(String[] args){Base base = new Sub();System.out.println(base.cout); // 10Sub sub = new Sub();System.out.println(base.cout); // 20}}class Base // 父类{int count = 10; // 属性}class Sub extends Base //子类{int count = 20; // 属性}public class Test { public static void main(String[] args) { Base base = new Sub(); System.out.println(base.cout); // 10 Sub sub = new Sub(); System.out.println(base.cout); // 20 } } class Base // 父类 { int count = 10; // 属性 } class Sub extends Base //子类 { int count = 20; // 属性 }public class Test { public static void main(String[] args) { Base base = new Sub(); System.out.println(base.cout); // 10 Sub sub = new Sub(); System.out.println(base.cout); // 20 } } class Base // 父类 { int count = 10; // 属性 } class Sub extends Base //子类 { int count = 20; // 属性 }
- instanceOf 比较操作符,用于判断对象的运行类型是否为XX类型或XX类型的子类型
Java的重要特性:动态绑定机制
- 当调用对象方法的时候,该方法会和该对象的内存地址/运行类型绑定
- 当调用对象属性时,没有动态绑定机制,哪里声明,哪里使用
例:
// main中A a = new B(); // 向上转型System.out.println(a.sum()); // 30System.out.println(a.sum1()); // 20//父类class A{public int i = 10;public int sum(){return getI()+10; // getI()是方法,调用时,与运行类型绑定}public int sum1(){return i + 10; // i 是属性,在A类里,或者说作用域就近原则,对应就是10}public int getI(){return i;}}//子类class B extends A{public int i = 20;public int getI(){return i;}}// main中 A a = new B(); // 向上转型 System.out.println(a.sum()); // 30 System.out.println(a.sum1()); // 20 //父类 class A { public int i = 10; public int sum() { return getI()+10; // getI()是方法,调用时,与运行类型绑定 } public int sum1() { return i + 10; // i 是属性,在A类里,或者说作用域就近原则,对应就是10 } public int getI() { return i; } } //子类 class B extends A { public int i = 20; public int getI() { return i; } }// main中 A a = new B(); // 向上转型 System.out.println(a.sum()); // 30 System.out.println(a.sum1()); // 20 //父类 class A { public int i = 10; public int sum() { return getI()+10; // getI()是方法,调用时,与运行类型绑定 } public int sum1() { return i + 10; // i 是属性,在A类里,或者说作用域就近原则,对应就是10 } public int getI() { return i; } } //子类 class B extends A { public int i = 20; public int getI() { return i; } }
多态的应用
-
多态数组
数组的定义类型为父类类型,里面保存的实际元素类型为子类类型 -
多态参数
方法定义的形参类型为父类类型,实参类型允许为子类类型
如有侵犯您的版权,请及时联系3500663466#qq.com(#换@),我们将第一时间删除本站数据。
暂无评论内容