Java基础入门篇
三、面向对象和JVM底层分析
3.4 面向对象的三大特征
面相对象的三大特征:封转、继承、多态。
(一)封装
“封装”(encapsulation),封装的理念是高内聚,低耦合。
- “高内聚”:封装细节,便于修改内部代码,提高代码可维护性
- “低耦合”:简化外部调用,便于使用者使用,便于扩展和协作
封装的优点:提高代码的安全性,提高代码的复用性
封装的实现——使用访问控制符,Java是使用“访问控制符”来控制哪些细节须要封装,哪些细节须要袒露。Java中的访问控制符:private、default、protected、public,访问权限:
- private(同一个类可用————类内友好)
- default(不定义默认就是default,同一个类、同一个包可用————包内友好)
- protected(同一个类、同一个包、子类可用————父子友好、包内友好)
- public(同一个类、同一个包、子类、所有类可用————全局友好)
若protected——若父类和子类“在同一个包中”,子类可以访问父类的protected成员,也可以访问父类new创建对象的protected成员。若protected——若父类和子类“不在同一个包中”,子类可以访问父类的protected成员,不可以访问父类new创建对象的protected成员。
- package BasicJava.encapsulation.a;
- /*
- * 测试封装
- */
- public class Person {
- private int testPrivate;
- int testDefault;
- protected int testProtected;
- public int testPublic;
- public void test(){
- System.out.println(this.testPrivate);
- }
- }
复制代码- package BasicJava.encapsulation.a;
- /*
- * 测试封装
- */
- // 同一个包内的不同类
- public class Student {
- public void study(){
- Person p = new Person();
- // System.out.println(p.testPrivate); // 无权访问
- System.out.println(p.testDefault);
- System.out.println(p.testProtected);
- System.out.println(p.testPublic);
- }
- }
- // 同一个包内具有继承关系的类
- class Student2 extends Person{
- public void study(){
- Person p = new Person();
- // System.out.println(p.testPrivate); // 无权访问
- System.out.println(p.testDefault);
- System.out.println(p.testProtected);
- System.out.println(p.testPublic);
- }
- }
复制代码 开发过程中封装的简单规则:
- 属性一般使用private,私有后提供相应的get/set方法来访问相干属性,这些方法通常是public修饰的(注意:boolean变量的get方法是is开头的)。
- 方法:一些只用于本类的辅助方法可以用private修饰,盼望其他类调用的方法可以用public修饰。
- package BasicJava.encapsulation.b;
- import BasicJava.encapsulation.a.Person;
- /*
- * 测试封装
- */
- public class Child extends Person {
- public void play(){
- Person p = new Person();
- // 此时由于是不同包的继承,所以不能使用父类对象的protected修饰的属性/方法
- //System.out.println(p.testProtected);
- System.out.println(super.testProtected); // 但是可以访问父类的protected成员
- }
- }
复制代码- package BasicJava.encapsulation.b;
- /*
- * 测试封装的简单规则
- */
- public class User {
- private int id;
- private String name;
- private boolean man;
- // private类型的属性通过get/set方法调用
- public int getId() {
- return id;
- }
- public void setId(int id) {
- this.id = id;
- }
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- public boolean isMan() {
- return man;
- }
- public void setMan(boolean man) {
- this.man = man;
- }
- }
复制代码- package BasicJava.encapsulation.b;
- /*
- * 测试封装的简单规则
- */
- public class TestUser {
- public static void main(String[] args) {
- User u = new User();
- u.setId(100);
- u.setName("小明");
- u.setMan(true);
- System.out.println(u.getId());
- System.out.println(u.getName());
- System.out.println(u.isMan());
- }
- }
复制代码
(二)继承
当我们不表现指定extends时,默认继承自父类java.lang.Object类,继承的作用:
- 代码复用,更加容易实现类的扩展,
- 方便建模,子类是父类的扩展
instanceof运算符是二元运算法,左边是对象,右边是类,用于判定当前对象是否是右边类或子类所创建的对象,是则返回true,反之返回false。
继承中父类又叫超类、基类,子类又叫派生类。Java中只有单继承,不像C++存在多继承,多继承会使得继承链过于复杂,系统难以维护引起紊乱。Java中类没有多继承但是接口有多继承,子类继承父类,可以得到父类全部的属性和方法(除了父类的构造方法),但是不见得可以直接访问,比方父类中的私有属性/方法。
- /*
- * 测试继承
- */
- public static class Person{
- String name;
- int height;
- public void rest(){
- System.out.println("休息一会!");
- }
- }
- public static class Student extends Person{
- String school;
- public Student(String name, int height, String school){
- this.name = name;
- this.height = height;
- this.school = school;
- }
- public void study(){
- System.out.println("学习!");
- }
- }
- public static void testExtends(){
- Student s1 = new Student("小明", 175, "HBU");
- s1.study();
- s1.rest();
- System.out.println(s1 instanceof Student);
- System.out.println(s1 instanceof Person);
- }
复制代码
super可以看作是堆父类对象的直接引用,可以通过super来访问父类中被子类覆盖的方法/属性。使用super调用普通方法,语句没有位置限定,可以在子类中任意使用。在一个类中,假如构造方法第一行没有调用super(…)大概this(…),那么Java默认调用super(),含义是调用父类的无参构造方法。
- /*
- * 使用super调用父类方法
- */
- public static class FatherClass{
- int value;
- void f(){
- value = 100;
- System.out.println("FatherClass's value is : " + value);
- }
- }
- public static class ChildClass extends FatherClass{
- int value;
- int age;
- void f(){
- // 调用父类的普通方法
- super.f();
- value = 200;
- System.out.println("ChildClass's value is : " + value);
- System.out.println(super.value);
- }
- }
- public static void testSuper(){
- ChildClass c1 = new ChildClass();
- c1.f();
- }
- }
复制代码
方法重写override与方法重载overload毫无关系。重写是指子类重写父类的方法可以用自身的举动替换父类的举动,重写是实现“多态”的须要条件。方法重写须要符合以下三个要点:
- “==”方法名、形参列表相同
- “≤”返回值类型和声明非常类型,子类小于等于父类
- “≥”访问权限,子类大于等于父类
除了继承,**“组合”**也可以实当代码的复用,组合的核心是将父类对象作为子类的属性。组合就是在子类中new一个父类对象,然后通过对象名+属性/方法的方式使用父类。和继承相比,组合更加灵活,继承只能有一个父类,但是组合可以new多个类,使用他们的属性。对于is-a的关系建议使用“继承”(Student is a Person),对于has-a的关系建议使用“组合”(Student has a bike)。
- /*
- * 测试方法重写override
- */
- public static class Vehicle{
- public void run(){
- System.out.println("跑!");
- }
- public Vehicle getVehicle(){
- System.out.println("给你一个交通工具!");
- return null;
- }
- }
- public static class Horse extends Vehicle{
- @Override
- public void run() {
- System.out.println("这是一匹马跑起来了!");
- }
- @Override
- public Horse getVehicle() {
- return new Horse();
- }
- }
- public static void testOverride(){
- Horse h = new Horse();
- h.run();
- h.getVehicle();
- }
复制代码
- /*
- * 实现组合
- */
- public static class Car{
- String name;
- int number;
- Car(){
- System.out.println("还没初始化");
- }
- Car(String name, int number){
- this.name = name;
- this.number = number;
- }
- public void run(){
- System.out.println(this.name + this.number + "出发!");
- }
- }
- public static class Bike{
- // 组合就是在子类中new一个父类对象,然后通过对象名+属性/方法的方式使用父类
- Car c = new Car();
- int wheel;
- public Bike(int wheel, String name, int number){
- this.wheel = wheel;
- this.c.name = name;
- this.c.number = number;
- }
- public void letCarGo(){
- System.out.println(this.c.name + this.c.number + "出发!");
- }
- public void letBikeGo(){
- System.out.println("这是一个"+ this.wheel + "轮车,出发!");
- }
- }
- public static void testCombine(){
- Car c1 = new Car("奔驰", 1001);
- c1.run();
- Bike b1 = new Bike(3, "丰田", 1002);
- b1.letCarGo();
- b1.letBikeGo();
- }
复制代码
属性/方法查找顺序,比方,查找变量h -> 查找当前类中有没有属性h -> 依次上溯每个父类 -> 查找每个父类中是否有h,直到Object -> 假如没有找到,则出现编译错误。
构造方法调用顺序,构造方法第一句总是super(…)来调用父类对应的构造方法,流程是:征象上追溯到Object类 -> 然后再次依次向下执行类的初始化块和构造方法 -> 直到当前子类为止。
静态初始化块的执行顺序和构造方法调用顺序一致。
- /*
- * 感受构造函数初始化顺序
- */
- class FatherClass2{
- //定义静态初始化块
- static{
- System.out.println("静态初始化块:FatherClass2");
- }
- // 无参构造器
- FatherClass2(){
- System.out.println("创建FatherClass2");
- }
- }
- class ChildClass2 extends FatherClass2{
- //定义静态初始化块
- static{
- System.out.println("静态初始化块:ChildClass2");
- }
- ChildClass2(){
- // 默认情况下是调用父类的无参构造器,等同于super();
- System.out.println("创建ChildClass2");
- }
- }
复制代码
(三)多态
“多态”(polymorphism):同一个方法调用,不同对象举动完全不同
多态的要点:
- 多态是方法的多态,与属性无关
- 多态的存在要有三个须要条件(继承、方法重写、父类引用指向子类对象)
- 父类引用指向子类对象后,用该父类引用调用子类重写的方法,就是多态
- package BasicJava.polymorphism;
- /*
- * 测试多态
- */
- public class Animal {
- public void shout(){
- System.out.println("动物的叫声");
- }
- }
- class Dog extends Animal{
- @Override
- public void shout() {
- System.out.println("汪汪汪!");
- }
- public void seeDoor(){
- System.out.println("正在看门...");
- }
- }
- class Cat extends Animal{
- @Override
- public void shout() {
- System.out.println("喵喵喵!");
- }
- public void catchMouse(){
- System.out.println("正在抓老鼠...");
- }
- }
复制代码 对象的转型(casting):由父类引用指向子类对象的过程中,我们称之为“向上转型”,属于“自动类型转换”。向上转型后的父类引用变量只能调用它“编译类型”的方法,不能调用它“运行时类型”的方法,这时我们须要进行强制类型转换,我们称之为“向下转型”。
- package BasicJava.polymorphism;
- public class TestPoly {
- public static void main(String[] args) {
- animalShout(new Dog()); // 相当于Animal a = new Dog(); 属于“向上转型”
- animalShout(new Cat()); // 相当于Animal a = new Cat(); 属于“向上转型”
- // new Dog()是我们真正的类型,Animal animal是我们定义的类型,因此无法执行Dog()类中的seeDoor()方法
- //编译类型 运行时类型
- Animal animal = new Dog();
- animal.shout(); // 可以执行
- // animal.seeDoor(); //无法执行
- // 通过“向下转型”就可以了,这里的d其实就是animal,只不过进行了强制类型转换
- Dog d = (Dog)animal;
- d.seeDoor();
- // 我们可以添加一个判断来避免异常
- if(animal instanceof Dog){
- Dog d2 = (Dog)animal;
- d2.seeDoor();
- }
- if(animal instanceof Cat){
- Cat c2 = (Cat)animal;
- c2.catchMouse();
- }
- // 测试用不同的类进行“向下转型”的强制类型转换
- Cat c = (Cat)animal;
- c.catchMouse(); // 发现并不报错,但是这实际上只是编译时不报错,但是运行的时候会报错的
- // "ClassCastException: BasicJava.polymorphism.Dog cannot be cast to BasicJava.polymorphism.Cat"
- }
- static void animalShout(Animal a){
- a.shout();
- }
- }
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |