封装、继承和多态
封装
-
程序设计追求“高内聚、低耦合”
-
高内聚:类的内部数据操作细节自己完成,不允许外部干涉
-
低耦合:仅暴露少量的方法供外部使用
-
封装即信息隐藏,通常在开发过程中,应禁止直接访问一个对象中数据的实际表示,而应该通过操作接口来访问,称为信息隐藏
-
属性私有,提供操作这些属性的方法(get/set方法)
-
封装能够提高程序的安全性,保护数据;隐藏程序代码的实现细节;统一接口;提高系统可维护性
-
程序案例:
public class Application { public static void main(String[] args) { Person person = new Person(); person.setPersonName("王小二"); System.out.println(person.getPersonName()); } } public class Person { /** 属性私有,只能是当前类使用 */ /** 人员姓名 */ private String personName; /** 人员年龄 */ private int personAge; /** 人员性别 */ private char personSex; /** 获取人员姓名属性值方法 */ public String getPersonName() { return personName; } /** 给人员姓名属性赋值方法 */ public void setPersonName(String personName) { this.personName = personName; } public int getPersonAge() { return personAge; } public void setPersonAge(int personAge) { this.personAge = personAge; } public char getPersonSex() { return personSex; } public void setPersonSex(char personSex) { this.personSex = personSex; } }
继承
-
继承的本质是对某一批类的抽象,从而实现对现实世界更好的建模
-
Java 中使用 extends 关键字来表示继承,extends 意为“扩展”,子类是父类的扩展
-
Java 中类只有单继承,没有多继承
-
继承是类和类之间的一种关系(类和类之间的关系还有依赖、组合、聚合等)
-
继承关系的两个类,一个为子类(派生类),一个为父类(基类),子类继承父类,使用关键字 extends 来表示
-
子类和父类之间,从某种意义上来讲应该是具有“ is a ”的关系
-
Java 中所有的类,都默认直接或间接继承 Object 类(上帝类)
-
子类继承了父类,则子类就拥有了所有父类的方法和属性(被 private 修饰的方法或属性除外,私有的属性和方法不能被继承)
-
IDEA 中可以通过Ctrl + h 来查看类的继承关系
-
super 关键字代表子类的父类对象,可以理解为指向自身父类对象的一个指针,this 关键字代表自身对象,可以理解为指向对象本身的指针
-
在子类中使用 super 关键字调用父类的构造方法,必须在子类构造方法的第一个
-
super 调用只能出现在子类的方法或构造方法中
-
super 和 this 不能同时调用构造方法
-
this() 自身类的构造方法super() 父类的构造方法
-
程序案例:
public class Application { public static void main(String[] args) { Man man = new Man(); } } public class People { public String name; private String money; public People() { System.out.println("这是人类!!!!!"); } public void eat() { System.out.println("吃东西!!!!!"); } } public class Man extends People { public String manAge; public Man() { // 子类构造方法中会隐式的加载父类构造方法super() // super(); super.eat(); // 子类不能继承父类私有的方法和属性 super.name = "王大"; System.out.println(super.name); System.out.println("这是男人!!!!!"); } public void play() { System.out.println("男人正在玩!!!!!"); } } public class Woman extends People { public long womanHair; public Woman() { super.eat(); System.out.println("这是女人!!!!!"); } public void longHair() { System.out.println("女人有长头发!!!!!"); } } // -------------------------------------- // 控制栏输出打印: /** 这是人类!!!!! 吃东西!!!!! 王大 这是男人!!!!! */
重写
-
重写针对的都是方法,和属性无关,且需要有继承关系
-
方法名称必须相同,参数列表必须相同,方法体可以不同,修饰符范围可以扩大,但不可以缩小:public > protected > default > private
-
静态方法不可以重写父类方法,创建子类对象使用父类接收时,调用的是父类的静态方法
-
非静态方法可以重写父类方法,创建子类对象使用父类接收时,调用的是子类的非静态方法
-
程序案例:
public class Application { public static void main(String[] args) { Man man1 = new Man(); // 静态方法 man1.reWrite(); // 输出 这是子类重写方法!!! // 非静态方法 man1.write(); // 输出 这是子类写方法!!! People man2 = new Man(); // 静态方法 man2.reWrite(); // 输出 这是父类重写方法!!! // 非静态方法 man2.write(); // 输出 这是子类写方法!!! } } public class Man extends People { public String manAge; public Man() { } public static void reWrite(){ System.out.println("这是子类重写方法!!!"); } public void write(){ System.out.println("这是子类写方法!!!"); } }
多态
-
同一方法可以根据发送对象的不同而采用多种不同的行为方式
-
一个对象的实际类型是确定的,但是可以指向对象的引用类型有很多(父类或有关系的类)
-
多态存在的条件1:类之间存在继承关系
-
多态存在的条件2:子类需要重写父类方法(static 修饰的方法(属于类不属于实例)、final 修饰的方法和 private 修饰的方法不能被重写)
-
多态存在的条件3:父类引用指向子类对象
-
多态是方法的多态,属性没有多态性
-
程序案例:
public class Application { public static void main(String[] args) { // 一个对象的实际类型是确定的 // new Student(); // new Person(); // 但是可以指向对象的引用类型有很多:父类的引用指向子类 Student student1 = new Student(); Person student2 = new Student(); Object student3 = new Student(); // 当子类没有run方法时,子类对象直接调用父类 run 方法, // 子类重写了父类 run 方法时,子类对象调用自身的 run 方法(尽管对象是被父类引用,也会走子类 run 方法) student1.run();// 输出 Son-run student2.run();// 输出 Son-run // 父类引用不能调用子类的方法 // 对象可以执行那些方法,主要看对象左边的类型,和右边关系不大 // student2.play(); // 程序报错不能编译 ((Student)student2).play(); // 这样强制转换是可以的 // Student 类(子类)能调用的方法都是自己的或者继承自父类的方法 // Person 类(父类)能指向子类,但是不能调用子类的方法 student1.play(); } } public class Person { public void run(){ System.out.println("Father-run"); } } public class Student extends Person { @Override public void run(){ System.out.println("Son-run"); } public void play(){ System.out.println("Son-play"); } }
instanceof
-
左边的对象是否是它右边的类的实例,返回 boolean 的数据类型
-
类型转换存在条件1:父类引用指向子类的对象
-
类型转换存在条件2:把子类转换为父类,向上转型,把父类转换为子类,向下转型(强制转换),会丢失子类自己的一些方法
-
对象类型转换的好处,方便方法调用,减少重复代码,使得程序更简洁
-
程序案例:
public class Application { public static void main(String[] args) { // Object > String // Object > Person > Teacher // Object > String > Student System.out.println("=======Object======="); Object studentObj = new Student(); System.out.println(studentObj instanceof Student); // 输出 true System.out.println(studentObj instanceof Person); // 输出 true System.out.println(studentObj instanceof Object); // 输出 true System.out.println(studentObj instanceof Teacher); // 输出 false System.out.println(studentObj instanceof String); // 输出 false System.out.println("=======Person======="); Person studentP = new Student(); System.out.println(studentP instanceof Student); // 输出 true System.out.println(studentP instanceof Person); // 输出 true System.out.println(studentP instanceof Object); // 输出 true System.out.println(studentP instanceof Teacher); // 输出 false // System.out.println(studentP instanceof String); // 编译报错 System.out.println("=======Student======="); Student Student = new Student(); System.out.println(Student instanceof Student); // 输出 true System.out.println(Student instanceof Person); // 输出 true System.out.println(Student instanceof Object); // 输出 true // System.out.println(Student instanceof Teacher); // 编译报错 // System.out.println(Student instanceof String); // 编译报错 // 编译是否报错 // 取决于 instanceof 左右两边类对象是否存在父与子的关系,存在编译成功,不存在编译报错 System.out.println("=======类型转换======="); // 子类可以直接转换为父类,但是转换后的对象不能调用子类自己的方法 Person sp = new Student(); // sp.studentRun(); // 编译报错 // 父类转换为子类,需要强制转换,但是会丢失子类自己本来的一些方法 ((Student)sp).studentRun(); // 输出 studentRun Student student = new Student(); student.studentRun(); // 输出 studentRun Person ps = student; // ps.studentRun(); // 编译报错 } } public class Person { public void fatherRun(){ System.out.println("fatherRun"); } } public class Teacher extends Person{ public void teacherRun(){ System.out.println("teacherRun"); } }